2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2010 Free Software Foundation Europe e.V.
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
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.
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
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.
29 * Match Bootstrap Records (used for restores) against
32 * Kern Sibbald, June MMII
38 * Also for efficiency, once a bsr is done, it really should be
39 * delinked from the bsr chain. This will avoid the above
40 * problem and make traversal of the bsr chain more efficient.
50 #include "lib/fnmatch.h"
53 const int dbglevel = 500;
55 /* Forward references */
56 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done);
57 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done);
58 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec);
59 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done);
60 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done);
61 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done);
62 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done);
63 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done);
64 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done);
65 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done);
66 static int match_voladdr(BSR *bsr, BSR_VOLADDR *voladdr, DEV_RECORD *rec, bool done);
67 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done);
68 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, bool done, JCR *jcr);
69 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block);
70 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block);
71 static BSR *find_smallest_volfile(BSR *fbsr, BSR *bsr);
74 /*********************************************************************
76 * If possible, position the archive device (tape) to read the
79 void position_bsr_block(BSR *bsr, DEV_BLOCK *block)
81 /* To be implemented */
84 /*********************************************************************
86 * Do fast block rejection based on bootstrap records.
87 * use_fast_rejection will be set if we have VolSessionId and VolSessTime
88 * in each record. When BlockVer is >= 2, we have those in the block header
89 * so can do fast rejection.
91 * returns: 1 if block may contain valid records
92 * 0 if block may be skipped (i.e. it contains no records of
93 * that can match the bsr).
96 int match_bsr_block(BSR *bsr, DEV_BLOCK *block)
98 if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
99 return 1; /* cannot fast reject */
102 for ( ; bsr; bsr=bsr->next) {
103 if (!match_block_sesstime(bsr, bsr->sesstime, block)) {
106 if (!match_block_sessid(bsr, bsr->sessid, block)) {
114 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block)
117 return 1; /* no specification matches all */
119 if (sesstime->sesstime == block->VolSessionTime) {
122 if (sesstime->next) {
123 return match_block_sesstime(bsr, sesstime->next, block);
128 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block)
131 return 1; /* no specification matches all */
133 if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
137 return match_block_sessid(bsr, sessid->next, block);
142 static int match_fileregex(BSR *bsr, DEV_RECORD *rec, JCR *jcr)
144 if (bsr->fileregex_re == NULL)
147 if (bsr->attr == NULL) {
148 bsr->attr = new_attr(jcr);
152 * The code breaks if the first record associated with a file is
155 if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
156 rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
157 bsr->skip_file = false;
158 if (unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, bsr->attr)) {
159 if (regexec(bsr->fileregex_re, bsr->attr->fname, 0, NULL, 0) == 0) {
160 Dmsg2(dbglevel, "Matched pattern, fname=%s FI=%d\n",
161 bsr->attr->fname, rec->FileIndex);
163 Dmsg2(dbglevel, "Didn't match, skipping fname=%s FI=%d\n",
164 bsr->attr->fname, rec->FileIndex);
165 bsr->skip_file = true;
172 /*********************************************************************
174 * Match Bootstrap records
176 * returns 0 no match and reposition is set if we should
177 * reposition the tape
178 * returns -1 no additional matches possible
180 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, JCR *jcr)
185 * The bsr->reposition flag is set any time a bsr is done.
186 * In this case, we can probably reposition the
187 * tape to the next available bsr position.
190 bsr->reposition = false;
191 stat = match_all(bsr, rec, volrec, sessrec, true, jcr);
193 * Note, bsr->reposition is set by match_all when
194 * a bsr is done. We turn it off if a match was
195 * found or if we cannot use positioning
197 if (stat != 0 || !bsr->use_positioning) {
198 bsr->reposition = false;
201 stat = 1; /* no bsr => match all */
207 * Find the next bsr that applies to the current tape.
208 * It is the one with the smallest VolFile position.
210 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
213 BSR *found_bsr = NULL;
215 /* Do tape/disk seeking only if CAP_POSITIONBLOCKS is on */
217 Dmsg0(dbglevel, "NULL root bsr pointer passed to find_next_bsr.\n");
220 if (!root_bsr->use_positioning ||
221 !root_bsr->reposition || !dev->has_cap(CAP_POSITIONBLOCKS)) {
222 Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
225 Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
226 root_bsr->mount_next_volume = false;
227 /* Walk through all bsrs to find the next one to use => smallest file,block */
228 for (bsr=root_bsr; bsr; bsr=bsr->next) {
229 if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
232 if (found_bsr == NULL) {
235 found_bsr = find_smallest_volfile(found_bsr, bsr);
239 * If we get to this point and found no bsr, it means
240 * that any additional bsr's must apply to the next
241 * tape, so set a flag.
243 if (found_bsr == NULL) {
244 root_bsr->mount_next_volume = true;
250 * Get the smallest address from this voladdr part
251 * Don't use "done" elements
253 static bool get_smallest_voladdr(BSR_VOLADDR *va, uint64_t *ret)
258 for (; va ; va = va->next) {
261 min_val = MIN(min_val, va->saddr);
273 * This routine needs to be fixed to only look at items that
274 * are not marked as done. Otherwise, it can find a bsr
275 * that has already been consumed, and this will cause the
276 * bsr to be used, thus we may seek back and re-read the
277 * same records, causing an error. This deficiency must
278 * be fixed. For the moment, it has been kludged in
279 * read_record.c to avoid seeking back if find_next_bsr
280 * returns a bsr pointing to a smaller address (file/block).
283 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
285 BSR *return_bsr = found_bsr;
288 uint32_t found_bsr_sfile, bsr_sfile;
289 uint32_t found_bsr_sblock, bsr_sblock;
290 uint64_t found_bsr_saddr, bsr_saddr;
292 /* if we have VolAddr, use it, else try with File and Block */
293 if (get_smallest_voladdr(found_bsr->voladdr, &found_bsr_saddr)) {
294 if (get_smallest_voladdr(bsr->voladdr, &bsr_saddr)) {
295 if (found_bsr_saddr > bsr_saddr) {
303 /* Find the smallest file in the found_bsr */
304 vf = found_bsr->volfile;
305 found_bsr_sfile = vf->sfile;
306 while ( (vf=vf->next) ) {
307 if (vf->sfile < found_bsr_sfile) {
308 found_bsr_sfile = vf->sfile;
312 /* Find the smallest file in the bsr */
314 bsr_sfile = vf->sfile;
315 while ( (vf=vf->next) ) {
316 if (vf->sfile < bsr_sfile) {
317 bsr_sfile = vf->sfile;
321 /* if the bsr file is less than the found_bsr file, return bsr */
322 if (found_bsr_sfile > bsr_sfile) {
324 } else if (found_bsr_sfile == bsr_sfile) {
325 /* Files are equal */
326 /* find smallest block in found_bsr */
327 vb = found_bsr->volblock;
328 found_bsr_sblock = vb->sblock;
329 while ( (vb=vb->next) ) {
330 if (vb->sblock < found_bsr_sblock) {
331 found_bsr_sblock = vb->sblock;
334 /* Find smallest block in bsr */
336 bsr_sblock = vb->sblock;
337 while ( (vb=vb->next) ) {
338 if (vb->sblock < bsr_sblock) {
339 bsr_sblock = vb->sblock;
342 /* Compare and return the smallest */
343 if (found_bsr_sblock > bsr_sblock) {
351 * Called after the signature record so that
352 * we can see if the current bsr has been
353 * fully processed (i.e. is done).
354 * The bsr argument is not used, but is included
355 * for consistency with the other match calls.
357 * Returns: true if we should reposition
360 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
362 BSR *rbsr = rec->bsr;
363 Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
369 if (rbsr->count && rbsr->found >= rbsr->count) {
371 rbsr->root->reposition = true;
372 Dmsg2(dbglevel, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
373 rbsr->count, rbsr->found);
376 Dmsg2(dbglevel, "is_end_this_bsr not done count=%d found=%d\n",
377 rbsr->count, rbsr->found);
382 * Match all the components of current record
385 * returns -1 no additional matches possible
387 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
388 SESSION_LABEL *sessrec, bool done, JCR *jcr)
390 Dmsg0(dbglevel, "Enter match_all\n");
392 // Dmsg0(dbglevel, "bsr->done set\n");
395 if (!match_volume(bsr, bsr->volume, volrec, 1)) {
396 Dmsg2(dbglevel, "bsr fail bsr_vol=%s != rec read_vol=%s\n", bsr->volume->VolumeName,
400 Dmsg2(dbglevel, "OK bsr match bsr_vol=%s read_vol=%s\n", bsr->volume->VolumeName,
403 if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
405 Dmsg3(dbglevel, "Fail on file=%u. bsr=%u,%u\n",
406 rec->File, bsr->volfile->sfile, bsr->volfile->efile);
411 if (!match_voladdr(bsr, bsr->voladdr, rec, 1)) {
413 Dmsg3(dbglevel, "Fail on Addr=%llu. bsr=%llu,%llu\n",
414 get_record_address(rec), bsr->voladdr->saddr, bsr->voladdr->eaddr);
419 if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
420 Dmsg2(dbglevel, "Fail on sesstime. bsr=%u rec=%u\n",
421 bsr->sesstime->sesstime, rec->VolSessionTime);
425 /* NOTE!! This test MUST come after the sesstime test */
426 if (!match_sessid(bsr, bsr->sessid, rec)) {
427 Dmsg2(dbglevel, "Fail on sessid. bsr=%u rec=%u\n",
428 bsr->sessid->sessid, rec->VolSessionId);
432 /* NOTE!! This test MUST come after sesstime and sessid tests */
433 if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
434 Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
435 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
438 Dmsg3(dbglevel, "match on findex=%d. bsr=%d,%d\n",
439 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
441 if (!match_fileregex(bsr, rec, jcr)) {
442 Dmsg1(dbglevel, "Fail on fileregex='%s'\n", bsr->fileregex);
446 /* This flag is set by match_fileregex (and perhaps other tests) */
447 if (bsr->skip_file) {
448 Dmsg1(dbglevel, "Skipping findex=%d\n", rec->FileIndex);
453 * If a count was specified and we have a FileIndex, assume
454 * it is a Bacula created bsr (or the equivalent). We
455 * then save the bsr where the match occurred so that
456 * after processing the record or records, we can update
457 * the found count. I.e. rec->bsr points to the bsr that
458 * satisfied the match.
460 if (bsr->count && bsr->FileIndex) {
462 Dmsg0(dbglevel, "Leave match_all 1\n");
463 return 1; /* this is a complete match */
467 * The selections below are not used by Bacula's
468 * restore command, and don't work because of
469 * the rec->bsr = bsr optimization above.
471 if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
472 Dmsg0(dbglevel, "fail on JobId\n");
476 if (!match_job(bsr, bsr->job, sessrec, 1)) {
477 Dmsg0(dbglevel, "fail on Job\n");
480 if (!match_client(bsr, bsr->client, sessrec, 1)) {
481 Dmsg0(dbglevel, "fail on Client\n");
484 if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
485 Dmsg0(dbglevel, "fail on Job type\n");
488 if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
489 Dmsg0(dbglevel, "fail on Job level\n");
492 if (!match_stream(bsr, bsr->stream, rec, 1)) {
493 Dmsg0(dbglevel, "fail on stream\n");
500 return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done, jcr);
502 if (bsr->done && done) {
503 Dmsg0(dbglevel, "Leave match all -1\n");
506 Dmsg0(dbglevel, "Leave match all 0\n");
510 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
513 return 0; /* Volume must match */
515 if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
516 Dmsg1(dbglevel, "match_volume=%s\n", volrec->VolumeName);
520 return match_volume(bsr, volume->next, volrec, 1);
525 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
528 return 1; /* no specification matches all */
530 if (strcmp(client->ClientName, sessrec->ClientName) == 0) {
534 return match_client(bsr, client->next, sessrec, 1);
539 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
542 return 1; /* no specification matches all */
544 if (strcmp(job->Job, sessrec->Job) == 0) {
548 return match_job(bsr, job->next, sessrec, 1);
553 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
556 return 1; /* no specification matches all */
558 if (job_type->JobType == sessrec->JobType) {
561 if (job_type->next) {
562 return match_job_type(bsr, job_type->next, sessrec, 1);
567 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
570 return 1; /* no specification matches all */
572 if (job_level->JobLevel == sessrec->JobLevel) {
575 if (job_level->next) {
576 return match_job_level(bsr, job_level->next, sessrec, 1);
581 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
584 return 1; /* no specification matches all */
586 if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
590 return match_jobid(bsr, jobid->next, sessrec, 1);
595 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
598 return 1; /* no specification matches all */
601 * The following code is turned off because this should now work
602 * with disk files too, though since a "volfile" is 4GB, it does
603 * not improve performance much.
606 /* For the moment, these tests work only with tapes. */
607 if (!(rec->state & REC_ISTAPE)) {
608 return 1; /* All File records OK for this match */
610 Dmsg3(dbglevel, "match_volfile: sfile=%u efile=%u recfile=%u\n",
611 volfile->sfile, volfile->efile, rec->File);
613 if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
616 /* Once we get past last efile, we are done */
617 if (rec->File > volfile->efile) {
618 volfile->done = true; /* set local done */
621 return match_volfile(bsr, volfile->next, rec, volfile->done && done);
624 /* If we are done and all prior matches are done, this bsr is finished */
625 if (volfile->done && done) {
627 bsr->root->reposition = true;
628 Dmsg2(dbglevel, "bsr done from volfile rec=%u volefile=%u\n",
629 rec->File, volfile->efile);
634 static int match_voladdr(BSR *bsr, BSR_VOLADDR *voladdr, DEV_RECORD *rec, bool done)
637 return 1; /* no specification matches all */
642 /* For the moment, these tests work only with disk. */
643 if (rec->state & REC_ISTAPE) {
644 uint32_t sFile = (voladdr->saddr)>>32;
645 uint32_t eFile = (voladdr->eaddr)>>32;
646 if (sFile <= rec->File && eFile >= rec->File) {
653 uint64_t addr = get_record_address(rec);
654 Dmsg6(dbglevel, "match_voladdr: saddr=%llu eaddr=%llu recaddr=%llu sfile=%u efile=%u recfile=%u\n",
655 voladdr->saddr, voladdr->eaddr, addr, voladdr->saddr>>32, voladdr->eaddr>>32, addr>>32);
657 if (voladdr->saddr <= addr && voladdr->eaddr >= addr) {
660 /* Once we get past last eblock, we are done */
661 if (addr > voladdr->eaddr) {
662 voladdr->done = true; /* set local done */
665 return match_voladdr(bsr, voladdr->next, rec, voladdr->done && done);
668 /* If we are done and all prior matches are done, this bsr is finished */
669 if (voladdr->done && done) {
671 bsr->root->reposition = true;
672 Dmsg2(dbglevel, "bsr done from voladdr rec=%llu voleaddr=%llu\n",
673 addr, voladdr->eaddr);
679 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
682 return 1; /* no specification matches all */
684 if (stream->stream == rec->Stream) {
688 return match_stream(bsr, stream->next, rec, 1);
693 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
696 return 1; /* no specification matches all */
698 if (sesstime->sesstime == rec->VolSessionTime) {
701 if (rec->VolSessionTime > sesstime->sesstime) {
702 sesstime->done = true;
704 if (sesstime->next) {
705 return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
707 if (sesstime->done && done) {
709 bsr->root->reposition = true;
710 Dmsg0(dbglevel, "bsr done from sesstime\n");
716 * Note, we cannot mark bsr done based on session id because we may
717 * have interleaved records, and there may be more of what we want
720 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
723 return 1; /* no specification matches all */
725 if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
729 return match_sessid(bsr, sessid->next, rec);
735 * When reading the Volume, the Volume Findex (rec->FileIndex) always
736 * are found in sequential order. Thus we can make optimizations.
738 * ***FIXME*** optimizations
739 * We could optimize by removing the recursion.
741 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
744 return 1; /* no specification matches all */
747 if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
748 Dmsg3(dbglevel, "Match on findex=%d. bsrFIs=%d,%d\n",
749 rec->FileIndex, findex->findex, findex->findex2);
752 if (rec->FileIndex > findex->findex2) {
757 return match_findex(bsr, findex->next, rec, findex->done && done);
759 if (findex->done && done) {
761 bsr->root->reposition = true;
762 Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);
767 uint64_t get_bsr_start_addr(BSR *bsr, uint32_t *file, uint32_t *block)
769 uint64_t bsr_addr = 0;
770 uint32_t sfile = 0, sblock = 0;
774 bsr_addr = bsr->voladdr->saddr;
775 sfile = bsr_addr>>32;
776 sblock = (uint32_t)bsr_addr;
778 } else if (bsr->volfile && bsr->volblock) {
779 bsr_addr = (((uint64_t)bsr->volfile->sfile)<<32)|bsr->volblock->sblock;
780 sfile = bsr->volfile->sfile;
781 sblock = bsr->volblock->sblock;