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