]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/match_bsr.c
Update copyright
[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 #include "bacula.h"
39 #include "stored.h"
40 #ifdef HAVE_FNMATCH
41 #include <fnmatch.h>
42 #else
43 #include "lib/fnmatch.h"
44 #endif
45
46 /* Forward references */
47 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done);
48 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done);
49 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec);
50 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done);
51 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done);
52 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done);
53 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done);
54 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done);
55 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done);
56 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done);
57 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done);
58 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, bool done);
59 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block);
60 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block);
61 static BSR *find_smallest_volfile(BSR *fbsr, BSR *bsr);
62
63
64 /*********************************************************************
65  *
66  *  If possible, position the archive device (tape) to read the
67  *  next block.
68  */
69 void position_bsr_block(BSR *bsr, DEV_BLOCK *block)
70 {
71    /* To be implemented */
72 }
73
74 /*********************************************************************
75  *
76  *  Do fast block rejection based on bootstrap records.
77  *    use_fast_rejection will be set if we have VolSessionId and VolSessTime
78  *    in each record. When BlockVer is >= 2, we have those in the block header
79  *    so can do fast rejection.
80  *
81  *   returns:  1 if block may contain valid records
82  *             0 if block may be skipped (i.e. it contains no records of
83  *                  that can match the bsr).
84  *
85  */
86 int match_bsr_block(BSR *bsr, DEV_BLOCK *block)
87 {
88    if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
89       return 1;                       /* cannot fast reject */
90    }
91
92    for ( ; bsr; bsr=bsr->next) {
93       if (!match_block_sesstime(bsr, bsr->sesstime, block)) {
94          continue;
95       }
96       if (!match_block_sessid(bsr, bsr->sessid, block)) {
97          continue;
98       }
99       return 1;
100    }
101    return 0;
102 }
103
104 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block)
105 {
106    if (!sesstime) {
107       return 1;                       /* no specification matches all */
108    }
109    if (sesstime->sesstime == block->VolSessionTime) {
110       return 1;
111    }
112    if (sesstime->next) {
113       return match_block_sesstime(bsr, sesstime->next, block);
114    }
115    return 0;
116 }
117
118 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block)
119 {
120    if (!sessid) {
121       return 1;                       /* no specification matches all */
122    }
123    if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
124       return 1;
125    }
126    if (sessid->next) {
127       return match_block_sessid(bsr, sessid->next, block);
128    }
129    return 0;
130 }
131
132
133 /*********************************************************************
134  *
135  *      Match Bootstrap records
136  *        returns  1 on match
137  *        returns  0 no match and reposition is set if we should
138  *                      reposition the tape
139  *       returns -1 no additional matches possible
140  */
141 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec)
142 {
143    int stat;
144
145    /*
146     * The bsr->reposition flag is set any time a bsr is done.
147     *   In this case, we can probably reposition the
148     *   tape to the next available bsr position.
149     */
150    if (bsr) {
151       bsr->reposition = false;
152       stat = match_all(bsr, rec, volrec, sessrec, true);
153       /*
154        * Note, bsr->reposition is set by match_all when
155        *  a bsr is done. We turn it off if a match was
156        *  found or if we cannot use positioning
157        */
158       if (stat != 0 || !bsr->use_positioning) {
159          bsr->reposition = false;
160       }
161    } else {
162       stat = 1;                       /* no bsr => match all */
163    }
164    return stat;
165 }
166
167 /*
168  * Find the next bsr that applies to the current tape.
169  *   It is the one with the smallest VolFile position.
170  */
171 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
172 {
173    BSR *bsr;
174    BSR *found_bsr = NULL;
175    bool no_file_seek = !dev->is_tape();
176 #ifdef FILE_SEEK
177    no_file_seek = false;
178 #endif
179
180    if (!root_bsr || !root_bsr->use_positioning ||
181        !root_bsr->reposition || no_file_seek) {
182       Dmsg2(300, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
183       return NULL;
184    }
185    Dmsg2(300, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
186    root_bsr->mount_next_volume = false;
187    for (bsr=root_bsr; bsr; bsr=bsr->next) {
188       if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
189          continue;
190       }
191       if (found_bsr == NULL) {
192          found_bsr = bsr;
193       } else {
194          found_bsr = find_smallest_volfile(found_bsr, bsr);
195       }
196    }
197    /*
198     * If we get to this point and found no bsr, it means
199     *  that any additional bsr's must apply to the next
200     *  tape, so set a flag.
201     */
202    if (found_bsr == NULL) {
203       root_bsr->mount_next_volume = true;
204    }
205    return found_bsr;
206 }
207
208 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
209 {
210    BSR *return_bsr = found_bsr;
211    BSR_VOLFILE *vf;
212    BSR_VOLBLOCK *vb;
213    uint32_t found_bsr_sfile, bsr_sfile;
214    uint32_t found_bsr_sblock, bsr_sblock;
215
216    vf = found_bsr->volfile;
217    found_bsr_sfile = vf->sfile;
218    while ( (vf=vf->next) ) {
219       if (vf->sfile < found_bsr_sfile) {
220          found_bsr_sfile = vf->sfile;
221       }
222    }
223    vf = bsr->volfile;
224    bsr_sfile = vf->sfile;
225    while ( (vf=vf->next) ) {
226       if (vf->sfile < bsr_sfile) {
227          bsr_sfile = vf->sfile;
228       }
229    }
230    if (found_bsr_sfile > bsr_sfile) {
231       return_bsr = bsr;
232    } else if (found_bsr_sfile == bsr_sfile) {
233       /* Must check block */
234       vb = found_bsr->volblock;
235       found_bsr_sblock = vb->sblock;
236       while ( (vb=vb->next) ) {
237          if (vb->sblock < found_bsr_sblock) {
238             found_bsr_sblock = vb->sblock;
239          }
240       }
241       vb = bsr->volblock;
242       bsr_sblock = vb->sblock;
243       while ( (vb=vb->next) ) {
244          if (vb->sblock < bsr_sblock) {
245             bsr_sblock = vb->sblock;
246          }
247       }
248       if (found_bsr_sblock > bsr_sblock) {
249          return_bsr = bsr;
250       }
251    }
252
253    return return_bsr;
254 }
255
256 /*
257  * Called after the signature record so that
258  *   we can see if the current bsr has been
259  *   fully processed (i.e. is done).
260  *  The bsr argument is not used, but is included
261  *    for consistency with the other match calls.
262  *
263  * Returns: true if we should reposition
264  *        : false otherwise.
265  */
266 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
267 {
268    BSR *rbsr = rec->bsr;
269    Dmsg1(300, "match_set %d\n", rbsr != NULL);
270    if (!rbsr) {
271       return false;
272    }
273    rec->bsr = NULL;
274    rbsr->found++;
275    if (rbsr->count && rbsr->found >= rbsr->count) {
276       rbsr->done = true;
277       rbsr->root->reposition = true;
278       Dmsg2(500, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
279          rbsr->count, rbsr->found);
280       return true;
281    }
282    return false;
283 }
284
285 /*
286  * Match all the components of current record
287  *   returns  1 on match
288  *   returns  0 no match
289  *   returns -1 no additional matches possible
290  */
291 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
292                      SESSION_LABEL *sessrec, bool done)
293 {
294    if (bsr->done) {
295       Dmsg0(300, "bsr->done set\n");
296       goto no_match;
297    }
298    if (!match_volume(bsr, bsr->volume, volrec, 1)) {
299       Dmsg2(300, "bsr fail vol=%s != rec vol=%s\n", bsr->volume->VolumeName,
300             volrec->VolumeName);
301       goto no_match;
302    }
303    if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
304       Dmsg2(300, "Fail on file. bsr=%d rec=%d\n", bsr->volfile->efile,
305          rec->File);
306       goto no_match;
307    }
308    if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
309       Dmsg2(300, "Fail on sesstime. bsr=%d rec=%d\n",
310          bsr->sesstime->sesstime, rec->VolSessionTime);
311       goto no_match;
312    }
313
314    /* NOTE!! This test MUST come after the sesstime test */
315    if (!match_sessid(bsr, bsr->sessid, rec)) {
316       Dmsg2(300, "Fail on sessid. bsr=%d rec=%d\n",
317          bsr->sessid->sessid, rec->VolSessionId);
318       goto no_match;
319    }
320
321    /* NOTE!! This test MUST come after sesstime and sessid tests */
322    if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
323       Dmsg2(300, "Fail on findex. bsr=%d rec=%d\n",
324          bsr->FileIndex->findex2, rec->FileIndex);
325       goto no_match;
326    }
327    /*
328     * If a count was specified and we have a FileIndex, assume
329     *   it is a Bacula created bsr (or the equivalent). We
330     *   then save the bsr where the match occurred so that
331     *   after processing the record or records, we can update
332     *   the found count. I.e. rec->bsr points to the bsr that
333     *   satisfied the match.
334     */
335    if (bsr->count && bsr->FileIndex) {
336       rec->bsr = bsr;
337       return 1;                       /* this is a complete match */
338    }
339
340    /*
341     * The selections below are not used by Bacula's
342     *   restore command, and don't work because of
343     *   the rec->bsr = bsr optimization above.
344     */
345    if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
346       Dmsg0(300, "fail on JobId\n");
347       goto no_match;
348
349    }
350    if (!match_job(bsr, bsr->job, sessrec, 1)) {
351       Dmsg0(300, "fail on Job\n");
352       goto no_match;
353    }
354    if (!match_client(bsr, bsr->client, sessrec, 1)) {
355       Dmsg0(300, "fail on Client\n");
356       goto no_match;
357    }
358    if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
359       Dmsg0(300, "fail on Job type\n");
360       goto no_match;
361    }
362    if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
363       Dmsg0(300, "fail on Job level\n");
364       goto no_match;
365    }
366    if (!match_stream(bsr, bsr->stream, rec, 1)) {
367       Dmsg0(300, "fail on stream\n");
368       goto no_match;
369    }
370    return 1;
371
372 no_match:
373    if (bsr->next) {
374       return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done);
375    }
376    if (bsr->done && done) {
377       return -1;
378    }
379    return 0;
380 }
381
382 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
383 {
384    if (!volume) {
385       return 0;                       /* Volume must match */
386    }
387    if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
388       return 1;
389    }
390    if (volume->next) {
391       return match_volume(bsr, volume->next, volrec, 1);
392    }
393    return 0;
394 }
395
396 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
397 {
398    if (!client) {
399       return 1;                       /* no specification matches all */
400    }
401    if (fnmatch(client->ClientName, sessrec->ClientName, 0) == 0) {
402       return 1;
403    }
404    if (client->next) {
405       return match_client(bsr, client->next, sessrec, 1);
406    }
407    return 0;
408 }
409
410 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
411 {
412    if (!job) {
413       return 1;                       /* no specification matches all */
414    }
415    if (fnmatch(job->Job, sessrec->Job, 0) == 0) {
416       return 1;
417    }
418    if (job->next) {
419       return match_job(bsr, job->next, sessrec, 1);
420    }
421    return 0;
422 }
423
424 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
425 {
426    if (!job_type) {
427       return 1;                       /* no specification matches all */
428    }
429    if (job_type->JobType == sessrec->JobType) {
430       return 1;
431    }
432    if (job_type->next) {
433       return match_job_type(bsr, job_type->next, sessrec, 1);
434    }
435    return 0;
436 }
437
438 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
439 {
440    if (!job_level) {
441       return 1;                       /* no specification matches all */
442    }
443    if (job_level->JobLevel == sessrec->JobLevel) {
444       return 1;
445    }
446    if (job_level->next) {
447       return match_job_level(bsr, job_level->next, sessrec, 1);
448    }
449    return 0;
450 }
451
452 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
453 {
454    if (!jobid) {
455       return 1;                       /* no specification matches all */
456    }
457    if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
458       return 1;
459    }
460    if (jobid->next) {
461       return match_jobid(bsr, jobid->next, sessrec, 1);
462    }
463    return 0;
464 }
465
466 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
467 {
468    if (!volfile) {
469       return 1;                       /* no specification matches all */
470    }
471    /* For the moment, these tests work only with tapes. */
472    if (!(rec->state & REC_ISTAPE)) {
473       return 1;                       /* All File records OK for this match */
474    }
475 // Dmsg3(300, "match_volfile: sfile=%d efile=%d recfile=%d\n",
476 //             volfile->sfile, volfile->efile, rec->File);
477    if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
478       return 1;
479    }
480    /* Once we get past last efile, we are done */
481    if (rec->File > volfile->efile) {
482       volfile->done = true;              /* set local done */
483    }
484    if (volfile->next) {
485       return match_volfile(bsr, volfile->next, rec, volfile->done && done);
486    }
487
488    /* If we are done and all prior matches are done, this bsr is finished */
489    if (volfile->done && done) {
490       bsr->done = true;
491       bsr->root->reposition = true;
492       Dmsg2(300, "bsr done from volfile rec=%d volefile=%d\n",
493          rec->File, volfile->efile);
494    }
495    return 0;
496 }
497
498 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
499 {
500    if (!stream) {
501       return 1;                       /* no specification matches all */
502    }
503    if (stream->stream == rec->Stream) {
504       return 1;
505    }
506    if (stream->next) {
507       return match_stream(bsr, stream->next, rec, 1);
508    }
509    return 0;
510 }
511
512 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
513 {
514    if (!sesstime) {
515       return 1;                       /* no specification matches all */
516    }
517    if (sesstime->sesstime == rec->VolSessionTime) {
518       return 1;
519    }
520    if (rec->VolSessionTime > sesstime->sesstime) {
521       sesstime->done = true;
522    }
523    if (sesstime->next) {
524       return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
525    }
526    if (sesstime->done && done) {
527       bsr->done = true;
528       bsr->root->reposition = true;
529       Dmsg0(300, "bsr done from sesstime\n");
530    }
531    return 0;
532 }
533
534 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
535 {
536    if (!sessid) {
537       return 1;                       /* no specification matches all */
538    }
539    if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
540       return 1;
541    }
542    if (sessid->next) {
543       return match_sessid(bsr, sessid->next, rec);
544    }
545    return 0;
546 }
547
548 /*
549  * When reading the Volume, the Volume Findex (rec->FileIndex) always
550  *   are found in sequential order. Thus we can make optimizations.
551  *
552  *  ***FIXME*** optimizations
553  * We could optimize a lot here by removing the recursion, and 
554  *   stopping the search earlier -- say when rec->FileIndex > findex->findex2
555  *   and findex->next == NULL.  Also, the current entry tests could be skipped
556  *   if findex->done is set.
557  */
558 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
559 {
560    if (!findex) {
561       return 1;                       /* no specification matches all */
562    }
563    if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
564       return 1;
565    }
566    if (rec->FileIndex > findex->findex2) {
567       findex->done = true;
568    }
569    if (findex->next) {
570       return match_findex(bsr, findex->next, rec, findex->done && done);
571    }
572    if (findex->done && done) {
573       bsr->done = true;
574       bsr->root->reposition = true;
575       Dmsg1(300, "bsr done from findex %d\n", rec->FileIndex);
576    }
577    return 0;
578 }