1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2002-2010 Free Software Foundation Europe e.V.
5 Copyright (C) 2016-2016 Bareos GmbH & Co. KG
6
7 This program is Free Software; you can redistribute it and/or
8 modify it under the terms of version three of the GNU Affero General Public
9 License as published by the Free Software Foundation and included
10 in the file LICENSE.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21 */
22 /*
23 * Kern Sibbald, June MMII
24 */
25
26 /**
27 * @file
28 * Match Bootstrap Records (used for restores) against
29 * Volume Records
30 */
31
32 /*
33 * ***FIXME***
34 * Also for efficiency, once a bsr is done, it really should be
35 * delinked from the bsr chain. This will avoid the above
36 * problem and make traversal of the bsr chain more efficient.
37 *
38 * To be done ...
39 */
40
41 #include "include/bareos.h"
42 #include "stored/bsr.h"
43 #include "stored/stored.h"
44 #include "include/jcr.h"
45
46 namespace storagedaemon {
47
48 const int dbglevel = 500;
49
50 /* Forward references */
51 static int MatchVolume(BootStrapRecord *bsr, BsrVolume *volume, VOLUME_LABEL *volrec, bool done);
52 static int MatchSesstime(BootStrapRecord *bsr, BsrSessionTime *sesstime, DeviceRecord *rec, bool done);
53 static int MatchSessid(BootStrapRecord *bsr, BsrSessionId *sessid, DeviceRecord *rec);
54 static int MatchClient(BootStrapRecord *bsr, BsrClient *client, SESSION_LABEL *sessrec, bool done);
55 static int MatchJob(BootStrapRecord *bsr, BsrJob *job, SESSION_LABEL *sessrec, bool done);
56 static int MatchJobType(BootStrapRecord *bsr, BsrJobType *job_type, SESSION_LABEL *sessrec, bool done);
57 static int MatchJobLevel(BootStrapRecord *bsr, BsrJoblevel *job_level, SESSION_LABEL *sessrec, bool done);
58 static int MatchJobid(BootStrapRecord *bsr, BsrJobid *jobid, SESSION_LABEL *sessrec, bool done);
59 static int MatchFindex(BootStrapRecord *bsr, BsrFileIndex *findex, DeviceRecord *rec, bool done);
60 static int MatchVolfile(BootStrapRecord *bsr, BsrVolumeFile *volfile, DeviceRecord *rec, bool done);
61 static int MatchVoladdr(BootStrapRecord *bsr, BsrVolumeAddress *voladdr, DeviceRecord *rec, bool done);
62 static int MatchStream(BootStrapRecord *bsr, BsrStream *stream, DeviceRecord *rec, bool done);
63 static int MatchAll(BootStrapRecord *bsr, DeviceRecord *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, bool done, JobControlRecord *jcr);
64 static int MatchBlockSesstime(BootStrapRecord *bsr, BsrSessionTime *sesstime, DeviceBlock *block);
65 static int MatchBlockSessid(BootStrapRecord *bsr, BsrSessionId *sessid, DeviceBlock *block);
66 static BootStrapRecord *find_smallest_volfile(BootStrapRecord *fbsr, BootStrapRecord *bsr);
67
68 /**
69 *
70 * If possible, position the archive device (tape) to read the
71 * next block.
72 */
PositionBsrBlock(BootStrapRecord * bsr,DeviceBlock * block)73 void PositionBsrBlock(BootStrapRecord *bsr, DeviceBlock *block)
74 {
75 /* To be implemented */
76 }
77
78 /**
79 *
80 * Do fast block rejection based on bootstrap records.
81 * use_fast_rejection will be set if we have VolSessionId and VolSessTime
82 * in each record. When BlockVer is >= 2, we have those in the block header
83 * so can do fast rejection.
84 *
85 * returns: 1 if block may contain valid records
86 * 0 if block may be skipped (i.e. it contains no records of
87 * that can match the bsr).
88 *
89 */
MatchBsrBlock(BootStrapRecord * bsr,DeviceBlock * block)90 int MatchBsrBlock(BootStrapRecord *bsr, DeviceBlock *block)
91 {
92 if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
93 return 1; /* cannot fast reject */
94 }
95
96 for ( ; bsr; bsr=bsr->next) {
97 if (!MatchBlockSesstime(bsr, bsr->sesstime, block)) {
98 continue;
99 }
100 if (!MatchBlockSessid(bsr, bsr->sessid, block)) {
101 continue;
102 }
103 return 1;
104 }
105 return 0;
106 }
107
MatchBlockSesstime(BootStrapRecord * bsr,BsrSessionTime * sesstime,DeviceBlock * block)108 static int MatchBlockSesstime(BootStrapRecord *bsr, BsrSessionTime *sesstime, DeviceBlock *block)
109 {
110 if (!sesstime) {
111 return 1; /* no specification matches all */
112 }
113 if (sesstime->sesstime == block->VolSessionTime) {
114 return 1;
115 }
116 if (sesstime->next) {
117 return MatchBlockSesstime(bsr, sesstime->next, block);
118 }
119 return 0;
120 }
121
MatchBlockSessid(BootStrapRecord * bsr,BsrSessionId * sessid,DeviceBlock * block)122 static int MatchBlockSessid(BootStrapRecord *bsr, BsrSessionId *sessid, DeviceBlock *block)
123 {
124 if (!sessid) {
125 return 1; /* no specification matches all */
126 }
127 if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
128 return 1;
129 }
130 if (sessid->next) {
131 return MatchBlockSessid(bsr, sessid->next, block);
132 }
133 return 0;
134 }
135
MatchFileregex(BootStrapRecord * bsr,DeviceRecord * rec,JobControlRecord * jcr)136 static int MatchFileregex(BootStrapRecord *bsr, DeviceRecord *rec, JobControlRecord *jcr)
137 {
138 if (bsr->fileregex_re == NULL)
139 return 1;
140
141 if (bsr->attr == NULL) {
142 bsr->attr = new_attr(jcr);
143 }
144
145 /*
146 * The code breaks if the first record associated with a file is
147 * not of this type
148 */
149 if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES ||
150 rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX) {
151 bsr->skip_file = false;
152 if (UnpackAttributesRecord(jcr, rec->Stream, rec->data, rec->data_len, bsr->attr)) {
153 if (regexec(bsr->fileregex_re, bsr->attr->fname, 0, NULL, 0) == 0) {
154 Dmsg2(dbglevel, "Matched pattern, fname=%s FI=%d\n",
155 bsr->attr->fname, rec->FileIndex);
156 } else {
157 Dmsg2(dbglevel, "Didn't match, skipping fname=%s FI=%d\n",
158 bsr->attr->fname, rec->FileIndex);
159 bsr->skip_file = true;
160 }
161 }
162 }
163 return 1;
164 }
165
166 /**
167 *
168 * Match Bootstrap records
169 * returns 1 on match
170 * returns 0 no match and Reposition is set if we should
171 * Reposition the tape
172 * returns -1 no additional matches possible
173 */
MatchBsr(BootStrapRecord * bsr,DeviceRecord * rec,VOLUME_LABEL * volrec,SESSION_LABEL * sessrec,JobControlRecord * jcr)174 int MatchBsr(BootStrapRecord *bsr, DeviceRecord *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, JobControlRecord *jcr)
175 {
176 int status;
177
178 /*
179 * The bsr->Reposition flag is set any time a bsr is done.
180 * In this case, we can probably Reposition the
181 * tape to the next available bsr position.
182 */
183 if (bsr) {
184 bsr->Reposition = false;
185 status = MatchAll(bsr, rec, volrec, sessrec, true, jcr);
186 /*
187 * Note, bsr->Reposition is set by MatchAll when
188 * a bsr is done. We turn it off if a match was
189 * found or if we cannot use positioning
190 */
191 if (status != 0 || !bsr->use_positioning) {
192 bsr->Reposition = false;
193 }
194 } else {
195 status = 1; /* no bsr => match all */
196 }
197 return status;
198 }
199
200 /**
201 * Find the next bsr that applies to the current tape.
202 * It is the one with the smallest VolFile position.
203 */
find_next_bsr(BootStrapRecord * root_bsr,Device * dev)204 BootStrapRecord *find_next_bsr(BootStrapRecord *root_bsr, Device *dev)
205 {
206 BootStrapRecord *bsr;
207 BootStrapRecord *found_bsr = NULL;
208
209 /* Do tape/disk seeking only if CAP_POSITIONBLOCKS is on */
210 if (!root_bsr) {
211 Dmsg0(dbglevel, "NULL root bsr pointer passed to find_next_bsr.\n");
212 return NULL;
213 }
214 if (!root_bsr->use_positioning ||
215 !root_bsr->Reposition || !dev->HasCap(CAP_POSITIONBLOCKS)) {
216 Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->Reposition);
217 return NULL;
218 }
219 Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->Reposition);
220 root_bsr->mount_next_volume = false;
221 /* Walk through all bsrs to find the next one to use => smallest file,block */
222 for (bsr=root_bsr; bsr; bsr=bsr->next) {
223 if (bsr->done || !MatchVolume(bsr, bsr->volume, &dev->VolHdr, 1)) {
224 continue;
225 }
226 if (found_bsr == NULL) {
227 found_bsr = bsr;
228 } else {
229 found_bsr = find_smallest_volfile(found_bsr, bsr);
230 }
231 }
232 /*
233 * If we get to this point and found no bsr, it means
234 * that any additional bsr's must apply to the next
235 * tape, so set a flag.
236 */
237 if (found_bsr == NULL) {
238 root_bsr->mount_next_volume = true;
239 }
240 return found_bsr;
241 }
242
243 /**
244 * Get the smallest address from this voladdr part
245 * Don't use "done" elements
246 */
GetSmallestVoladdr(BsrVolumeAddress * va,uint64_t * ret)247 static bool GetSmallestVoladdr(BsrVolumeAddress *va, uint64_t *ret)
248 {
249 bool ok=false;
250 uint64_t min_val=0;
251
252 for (; va ; va = va->next) {
253 if (!va->done) {
254 if (ok) {
255 min_val = MIN(min_val, va->saddr);
256 } else {
257 min_val = va->saddr;
258 ok=true;
259 }
260 }
261 }
262 *ret = min_val;
263 return ok;
264 }
265
266 /* FIXME
267 * This routine needs to be fixed to only look at items that
268 * are not marked as done. Otherwise, it can find a bsr
269 * that has already been consumed, and this will cause the
270 * bsr to be used, thus we may seek back and re-read the
271 * same records, causing an error. This deficiency must
272 * be fixed. For the moment, it has been kludged in
273 * read_record.c to avoid seeking back if find_next_bsr
274 * returns a bsr pointing to a smaller address (file/block).
275 *
276 */
find_smallest_volfile(BootStrapRecord * found_bsr,BootStrapRecord * bsr)277 static BootStrapRecord *find_smallest_volfile(BootStrapRecord *found_bsr, BootStrapRecord *bsr)
278 {
279 BootStrapRecord *return_bsr = found_bsr;
280 BsrVolumeFile *vf;
281 BsrVolumeBlock *vb;
282 uint32_t found_bsr_sfile, bsr_sfile;
283 uint32_t found_bsr_sblock, bsr_sblock;
284 uint64_t found_bsr_saddr, bsr_saddr;
285
286 /* if we have VolAddr, use it, else try with File and Block */
287 if (GetSmallestVoladdr(found_bsr->voladdr, &found_bsr_saddr)) {
288 if (GetSmallestVoladdr(bsr->voladdr, &bsr_saddr)) {
289 if (found_bsr_saddr > bsr_saddr) {
290 return bsr;
291 } else {
292 return found_bsr;
293 }
294 }
295 }
296
297 /* Find the smallest file in the found_bsr */
298 vf = found_bsr->volfile;
299 found_bsr_sfile = vf->sfile;
300 while ( (vf=vf->next) ) {
301 if (vf->sfile < found_bsr_sfile) {
302 found_bsr_sfile = vf->sfile;
303 }
304 }
305
306 /* Find the smallest file in the bsr */
307 vf = bsr->volfile;
308 bsr_sfile = vf->sfile;
309 while ( (vf=vf->next) ) {
310 if (vf->sfile < bsr_sfile) {
311 bsr_sfile = vf->sfile;
312 }
313 }
314
315 /* if the bsr file is less than the found_bsr file, return bsr */
316 if (found_bsr_sfile > bsr_sfile) {
317 return_bsr = bsr;
318 } else if (found_bsr_sfile == bsr_sfile) {
319 /* Files are equal */
320 /* find smallest block in found_bsr */
321 vb = found_bsr->volblock;
322 found_bsr_sblock = vb->sblock;
323 while ( (vb=vb->next) ) {
324 if (vb->sblock < found_bsr_sblock) {
325 found_bsr_sblock = vb->sblock;
326 }
327 }
328 /* Find smallest block in bsr */
329 vb = bsr->volblock;
330 bsr_sblock = vb->sblock;
331 while ( (vb=vb->next) ) {
332 if (vb->sblock < bsr_sblock) {
333 bsr_sblock = vb->sblock;
334 }
335 }
336 /* Compare and return the smallest */
337 if (found_bsr_sblock > bsr_sblock) {
338 return_bsr = bsr;
339 }
340 }
341 return return_bsr;
342 }
343
344 /**
345 * Called after the signature record so that
346 * we can see if the current bsr has been
347 * fully processed (i.e. is done).
348 * The bsr argument is not used, but is included
349 * for consistency with the other match calls.
350 *
351 * Returns: true if we should reposition
352 * : false otherwise.
353 */
IsThisBsrDone(BootStrapRecord * bsr,DeviceRecord * rec)354 bool IsThisBsrDone(BootStrapRecord *bsr, DeviceRecord *rec)
355 {
356 BootStrapRecord *rbsr = rec->bsr;
357 Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
358 if (!rbsr) {
359 return false;
360 }
361 rec->bsr = NULL;
362 rbsr->found++;
363 if (rbsr->count && rbsr->found >= rbsr->count) {
364 rbsr->done = true;
365 rbsr->root->Reposition = true;
366 Dmsg2(dbglevel, "is_end_this_bsr set Reposition=1 count=%d found=%d\n",
367 rbsr->count, rbsr->found);
368 return true;
369 }
370 Dmsg2(dbglevel, "is_end_this_bsr not done count=%d found=%d\n",
371 rbsr->count, rbsr->found);
372 return false;
373 }
374
375 /**
376 * Match all the components of current record
377 * returns 1 on match
378 * returns 0 no match
379 * returns -1 no additional matches possible
380 */
MatchAll(BootStrapRecord * bsr,DeviceRecord * rec,VOLUME_LABEL * volrec,SESSION_LABEL * sessrec,bool done,JobControlRecord * jcr)381 static int MatchAll(BootStrapRecord *bsr, DeviceRecord *rec, VOLUME_LABEL *volrec,
382 SESSION_LABEL *sessrec, bool done, JobControlRecord *jcr)
383 {
384 Dmsg0(dbglevel, "Enter MatchAll\n");
385 if (bsr->done) {
386 // Dmsg0(dbglevel, "bsr->done set\n");
387 goto no_match;
388 }
389 if (!MatchVolume(bsr, bsr->volume, volrec, 1)) {
390 Dmsg2(dbglevel, "bsr fail bsr_vol=%s != rec read_vol=%s\n", bsr->volume->VolumeName,
391 volrec->VolumeName);
392 goto no_match;
393 }
394 Dmsg2(dbglevel, "OK bsr match bsr_vol=%s read_vol=%s\n", bsr->volume->VolumeName,
395 volrec->VolumeName);
396
397 if (!MatchVolfile(bsr, bsr->volfile, rec, 1)) {
398 if (bsr->volfile) {
399 Dmsg3(dbglevel, "Fail on file=%u. bsr=%u,%u\n",
400 rec->File, bsr->volfile->sfile, bsr->volfile->efile);
401 }
402 goto no_match;
403 }
404
405 if (!MatchVoladdr(bsr, bsr->voladdr, rec, 1)) {
406 if (bsr->voladdr) {
407 Dmsg3(dbglevel, "Fail on Addr=%llu. bsr=%llu,%llu\n",
408 GetRecordAddress(rec), bsr->voladdr->saddr, bsr->voladdr->eaddr);
409 }
410 goto no_match;
411 }
412
413 if (!MatchSesstime(bsr, bsr->sesstime, rec, 1)) {
414 Dmsg2(dbglevel, "Fail on sesstime. bsr=%u rec=%u\n",
415 bsr->sesstime->sesstime, rec->VolSessionTime);
416 goto no_match;
417 }
418
419 /* NOTE!! This test MUST come after the sesstime test */
420 if (!MatchSessid(bsr, bsr->sessid, rec)) {
421 Dmsg2(dbglevel, "Fail on sessid. bsr=%u rec=%u\n",
422 bsr->sessid->sessid, rec->VolSessionId);
423 goto no_match;
424 }
425
426 /* NOTE!! This test MUST come after sesstime and sessid tests */
427 if (!MatchFindex(bsr, bsr->FileIndex, rec, 1)) {
428 if (bsr->FileIndex) {
429 Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
430 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
431 } else {
432 Dmsg0(dbglevel, "No bsr->FileIndex!\n");
433 }
434 goto no_match;
435 }
436 Dmsg3(dbglevel, "match on findex=%d. bsr=%d,%d\n",
437 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
438
439 if (!MatchFileregex(bsr, rec, jcr)) {
440 Dmsg1(dbglevel, "Fail on fileregex='%s'\n", bsr->fileregex);
441 goto no_match;
442 }
443
444 /* This flag is set by MatchFileregex (and perhaps other tests) */
445 if (bsr->skip_file) {
446 Dmsg1(dbglevel, "Skipping findex=%d\n", rec->FileIndex);
447 goto no_match;
448 }
449
450 /*
451 * If a count was specified and we have a FileIndex, assume
452 * it is a Bareos created bsr (or the equivalent). We
453 * then save the bsr where the match occurred so that
454 * after processing the record or records, we can update
455 * the found count. I.e. rec->bsr points to the bsr that
456 * satisfied the match.
457 */
458 if (bsr->count && bsr->FileIndex) {
459 rec->bsr = bsr;
460 Dmsg0(dbglevel, "Leave MatchAll 1\n");
461 return 1; /* this is a complete match */
462 }
463
464 /*
465 * The selections below are not used by Bareos's
466 * restore command, and don't work because of
467 * the rec->bsr = bsr optimization above.
468 */
469 if (!MatchJobid(bsr, bsr->JobId, sessrec, 1)) {
470 Dmsg0(dbglevel, "fail on JobId\n");
471 goto no_match;
472
473 }
474 if (!MatchJob(bsr, bsr->job, sessrec, 1)) {
475 Dmsg0(dbglevel, "fail on Job\n");
476 goto no_match;
477 }
478 if (!MatchClient(bsr, bsr->client, sessrec, 1)) {
479 Dmsg0(dbglevel, "fail on Client\n");
480 goto no_match;
481 }
482 if (!MatchJobType(bsr, bsr->JobType, sessrec, 1)) {
483 Dmsg0(dbglevel, "fail on Job type\n");
484 goto no_match;
485 }
486 if (!MatchJobLevel(bsr, bsr->JobLevel, sessrec, 1)) {
487 Dmsg0(dbglevel, "fail on Job level\n");
488 goto no_match;
489 }
490 if (!MatchStream(bsr, bsr->stream, rec, 1)) {
491 Dmsg0(dbglevel, "fail on stream\n");
492 goto no_match;
493 }
494 return 1;
495
496 no_match:
497 if (bsr->next) {
498 return MatchAll(bsr->next, rec, volrec, sessrec, bsr->done && done, jcr);
499 }
500 if (bsr->done && done) {
501 Dmsg0(dbglevel, "Leave match all -1\n");
502 return -1;
503 }
504 Dmsg0(dbglevel, "Leave match all 0\n");
505 return 0;
506 }
507
MatchVolume(BootStrapRecord * bsr,BsrVolume * volume,VOLUME_LABEL * volrec,bool done)508 static int MatchVolume(BootStrapRecord *bsr, BsrVolume *volume, VOLUME_LABEL *volrec, bool done)
509 {
510 if (!volume) {
511 return 0; /* Volume must match */
512 }
513 if (bstrcmp(volume->VolumeName, volrec->VolumeName)) {
514 Dmsg1(dbglevel, "MatchVolume=%s\n", volrec->VolumeName);
515 return 1;
516 }
517 if (volume->next) {
518 return MatchVolume(bsr, volume->next, volrec, 1);
519 }
520 return 0;
521 }
522
MatchClient(BootStrapRecord * bsr,BsrClient * client,SESSION_LABEL * sessrec,bool done)523 static int MatchClient(BootStrapRecord *bsr, BsrClient *client, SESSION_LABEL *sessrec, bool done)
524 {
525 if (!client) {
526 return 1; /* no specification matches all */
527 }
528 if (bstrcmp(client->ClientName, sessrec->ClientName)) {
529 return 1;
530 }
531 if (client->next) {
532 return MatchClient(bsr, client->next, sessrec, 1);
533 }
534 return 0;
535 }
536
MatchJob(BootStrapRecord * bsr,BsrJob * job,SESSION_LABEL * sessrec,bool done)537 static int MatchJob(BootStrapRecord *bsr, BsrJob *job, SESSION_LABEL *sessrec, bool done)
538 {
539 if (!job) {
540 return 1; /* no specification matches all */
541 }
542 if (bstrcmp(job->Job, sessrec->Job)) {
543 return 1;
544 }
545 if (job->next) {
546 return MatchJob(bsr, job->next, sessrec, 1);
547 }
548 return 0;
549 }
550
MatchJobType(BootStrapRecord * bsr,BsrJobType * job_type,SESSION_LABEL * sessrec,bool done)551 static int MatchJobType(BootStrapRecord *bsr, BsrJobType *job_type, SESSION_LABEL *sessrec, bool done)
552 {
553 if (!job_type) {
554 return 1; /* no specification matches all */
555 }
556 if (job_type->JobType == sessrec->JobType) {
557 return 1;
558 }
559 if (job_type->next) {
560 return MatchJobType(bsr, job_type->next, sessrec, 1);
561 }
562 return 0;
563 }
564
MatchJobLevel(BootStrapRecord * bsr,BsrJoblevel * job_level,SESSION_LABEL * sessrec,bool done)565 static int MatchJobLevel(BootStrapRecord *bsr, BsrJoblevel *job_level, SESSION_LABEL *sessrec, bool done)
566 {
567 if (!job_level) {
568 return 1; /* no specification matches all */
569 }
570 if (job_level->JobLevel == sessrec->JobLevel) {
571 return 1;
572 }
573 if (job_level->next) {
574 return MatchJobLevel(bsr, job_level->next, sessrec, 1);
575 }
576 return 0;
577 }
578
MatchJobid(BootStrapRecord * bsr,BsrJobid * jobid,SESSION_LABEL * sessrec,bool done)579 static int MatchJobid(BootStrapRecord *bsr, BsrJobid *jobid, SESSION_LABEL *sessrec, bool done)
580 {
581 if (!jobid) {
582 return 1; /* no specification matches all */
583 }
584 if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
585 return 1;
586 }
587 if (jobid->next) {
588 return MatchJobid(bsr, jobid->next, sessrec, 1);
589 }
590 return 0;
591 }
592
MatchVolfile(BootStrapRecord * bsr,BsrVolumeFile * volfile,DeviceRecord * rec,bool done)593 static int MatchVolfile(BootStrapRecord *bsr, BsrVolumeFile *volfile, DeviceRecord *rec, bool done)
594 {
595 if (!volfile) {
596 return 1; /* no specification matches all */
597 }
598 /**
599 * The following code is turned off because this should now work
600 * with disk files too, though since a "volfile" is 4GB, it does
601 * not improve performance much.
602 */
603 #ifdef xxx
604 /* For the moment, these tests work only with tapes. */
605 if (!(rec->state & REC_ISTAPE)) {
606 return 1; /* All File records OK for this match */
607 }
608 Dmsg3(dbglevel, "MatchVolfile: sfile=%u efile=%u recfile=%u\n",
609 volfile->sfile, volfile->efile, rec->File);
610 #endif
611 if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
612 return 1;
613 }
614 /* Once we get past last efile, we are done */
615 if (rec->File > volfile->efile) {
616 volfile->done = true; /* set local done */
617 }
618 if (volfile->next) {
619 return MatchVolfile(bsr, volfile->next, rec, volfile->done && done);
620 }
621
622 /* If we are done and all prior matches are done, this bsr is finished */
623 if (volfile->done && done) {
624 bsr->done = true;
625 bsr->root->Reposition = true;
626 Dmsg2(dbglevel, "bsr done from volfile rec=%u volefile=%u\n",
627 rec->File, volfile->efile);
628 }
629 return 0;
630 }
631
MatchVoladdr(BootStrapRecord * bsr,BsrVolumeAddress * voladdr,DeviceRecord * rec,bool done)632 static int MatchVoladdr(BootStrapRecord *bsr, BsrVolumeAddress *voladdr, DeviceRecord *rec, bool done)
633 {
634 if (!voladdr) {
635 return 1; /* no specification matches all */
636 }
637
638 #ifdef xxx
639
640 /* For the moment, these tests work only with disk. */
641 if (rec->state & REC_ISTAPE) {
642 uint32_t sFile = (voladdr->saddr)>>32;
643 uint32_t eFile = (voladdr->eaddr)>>32;
644 if (sFile <= rec->File && eFile >= rec->File) {
645 return 1;
646 }
647 }
648
649 #endif
650
651 uint64_t addr = GetRecordAddress(rec);
652 Dmsg6(dbglevel, "MatchVoladdr: saddr=%llu eaddr=%llu recaddr=%llu sfile=%u efile=%u recfile=%u\n",
653 voladdr->saddr, voladdr->eaddr, addr, voladdr->saddr>>32, voladdr->eaddr>>32, addr>>32);
654
655 if (voladdr->saddr <= addr && voladdr->eaddr >= addr) {
656 return 1;
657 }
658 /* Once we get past last eblock, we are done */
659 if (addr > voladdr->eaddr) {
660 voladdr->done = true; /* set local done */
661 }
662 if (voladdr->next) {
663 return MatchVoladdr(bsr, voladdr->next, rec, voladdr->done && done);
664 }
665
666 /* If we are done and all prior matches are done, this bsr is finished */
667 if (voladdr->done && done) {
668 bsr->done = true;
669 bsr->root->Reposition = true;
670 Dmsg2(dbglevel, "bsr done from voladdr rec=%llu voleaddr=%llu\n",
671 addr, voladdr->eaddr);
672 }
673 return 0;
674 }
675
676
MatchStream(BootStrapRecord * bsr,BsrStream * stream,DeviceRecord * rec,bool done)677 static int MatchStream(BootStrapRecord *bsr, BsrStream *stream, DeviceRecord *rec, bool done)
678 {
679 if (!stream) {
680 return 1; /* no specification matches all */
681 }
682 if (stream->stream == rec->Stream) {
683 return 1;
684 }
685 if (stream->next) {
686 return MatchStream(bsr, stream->next, rec, 1);
687 }
688 return 0;
689 }
690
MatchSesstime(BootStrapRecord * bsr,BsrSessionTime * sesstime,DeviceRecord * rec,bool done)691 static int MatchSesstime(BootStrapRecord *bsr, BsrSessionTime *sesstime, DeviceRecord *rec, bool done)
692 {
693 if (!sesstime) {
694 return 1; /* no specification matches all */
695 }
696 if (sesstime->sesstime == rec->VolSessionTime) {
697 return 1;
698 }
699 if (rec->VolSessionTime > sesstime->sesstime) {
700 sesstime->done = true;
701 }
702 if (sesstime->next) {
703 return MatchSesstime(bsr, sesstime->next, rec, sesstime->done && done);
704 }
705 if (sesstime->done && done) {
706 bsr->done = true;
707 bsr->root->Reposition = true;
708 Dmsg0(dbglevel, "bsr done from sesstime\n");
709 }
710 return 0;
711 }
712
713 /**
714 * Note, we cannot mark bsr done based on session id because we may
715 * have interleaved records, and there may be more of what we want
716 * later.
717 */
MatchSessid(BootStrapRecord * bsr,BsrSessionId * sessid,DeviceRecord * rec)718 static int MatchSessid(BootStrapRecord *bsr, BsrSessionId *sessid, DeviceRecord *rec)
719 {
720 if (!sessid) {
721 return 1; /* no specification matches all */
722 }
723 if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
724 return 1;
725 }
726 if (sessid->next) {
727 return MatchSessid(bsr, sessid->next, rec);
728 }
729 return 0;
730 }
731
732 /**
733 * When reading the Volume, the Volume Findex (rec->FileIndex) always
734 * are found in sequential order. Thus we can make optimizations.
735 *
736 * ***FIXME*** optimizations
737 * We could optimize by removing the recursion.
738 */
MatchFindex(BootStrapRecord * bsr,BsrFileIndex * findex,DeviceRecord * rec,bool done)739 static int MatchFindex(BootStrapRecord *bsr, BsrFileIndex *findex, DeviceRecord *rec, bool done)
740 {
741 if (!findex) {
742 return 1; /* no specification matches all */
743 }
744 if (!findex->done) {
745 if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
746 Dmsg3(dbglevel, "Match on findex=%d. bsrFIs=%d,%d\n",
747 rec->FileIndex, findex->findex, findex->findex2);
748 return 1;
749 }
750 if (rec->FileIndex > findex->findex2) {
751 findex->done = true;
752 }
753 }
754 if (findex->next) {
755 return MatchFindex(bsr, findex->next, rec, findex->done && done);
756 }
757 if (findex->done && done) {
758 bsr->done = true;
759 bsr->root->Reposition = true;
760 Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);
761 }
762 return 0;
763 }
764
GetBsrStartAddr(BootStrapRecord * bsr,uint32_t * file,uint32_t * block)765 uint64_t GetBsrStartAddr(BootStrapRecord *bsr, uint32_t *file, uint32_t *block)
766 {
767 uint64_t bsr_addr = 0;
768 uint32_t sfile = 0, sblock = 0;
769
770 if (bsr) {
771 if (bsr->voladdr) {
772 bsr_addr = bsr->voladdr->saddr;
773 sfile = bsr_addr>>32;
774 sblock = (uint32_t)bsr_addr;
775
776 } else if (bsr->volfile && bsr->volblock) {
777 bsr_addr = (((uint64_t)bsr->volfile->sfile)<<32)|bsr->volblock->sblock;
778 sfile = bsr->volfile->sfile;
779 sblock = bsr->volblock->sblock;
780 }
781 }
782
783 if (file && block) {
784 *file = sfile;
785 *block = sblock;
786 }
787
788 return bsr_addr;
789 }
790
791 /* ****************************************************************
792 * Routines for handling volumes
793 */
new_restore_volume()794 static VolumeList *new_restore_volume()
795 {
796 VolumeList *vol;
797 vol = (VolumeList *)malloc(sizeof(VolumeList));
798 memset(vol, 0, sizeof(VolumeList));
799 return vol;
800 }
801
802 /**
803 * Add current volume to end of list, only if the Volume
804 * is not already in the list.
805 *
806 * returns: 1 if volume added
807 * 0 if volume already in list
808 */
AddRestoreVolume(JobControlRecord * jcr,VolumeList * vol)809 static bool AddRestoreVolume(JobControlRecord *jcr, VolumeList *vol)
810 {
811 VolumeList *next = jcr->VolList;
812
813 /* Add volume to volume manager's read list */
814 AddReadVolume(jcr, vol->VolumeName);
815
816 if (!next) { /* list empty ? */
817 jcr->VolList = vol; /* yes, add volume */
818 } else {
819 /* Loop through all but last */
820 for ( ; next->next; next=next->next) {
821 if (bstrcmp(vol->VolumeName, next->VolumeName)) {
822 /* Save smallest start file */
823 if (vol->start_file < next->start_file) {
824 next->start_file = vol->start_file;
825 }
826 return false; /* already in list */
827 }
828 }
829 /* Check last volume in list */
830 if (bstrcmp(vol->VolumeName, next->VolumeName)) {
831 if (vol->start_file < next->start_file) {
832 next->start_file = vol->start_file;
833 }
834 return false; /* already in list */
835 }
836 next->next = vol; /* add volume */
837 }
838 return true;
839 }
840
841 /**
842 * Create a list of Volumes (and Slots and Start positions) to be
843 * used in the current restore job.
844 */
CreateRestoreVolumeList(JobControlRecord * jcr)845 void CreateRestoreVolumeList(JobControlRecord *jcr)
846 {
847 char *p, *n;
848 VolumeList *vol;
849
850 /*
851 * Build a list of volumes to be processed
852 */
853 jcr->NumReadVolumes = 0;
854 jcr->CurReadVolume = 0;
855 if (jcr->bsr) {
856 BootStrapRecord *bsr = jcr->bsr;
857 if (!bsr->volume || !bsr->volume->VolumeName[0]) {
858 return;
859 }
860 for ( ; bsr; bsr=bsr->next) {
861 BsrVolume *bsrvol;
862 BsrVolumeFile *volfile;
863 uint32_t sfile = UINT32_MAX;
864
865 /* Find minimum start file so that we can forward space to it */
866 for (volfile = bsr->volfile; volfile; volfile=volfile->next) {
867 if (volfile->sfile < sfile) {
868 sfile = volfile->sfile;
869 }
870 }
871 /* Now add volumes for this bsr */
872 for (bsrvol = bsr->volume; bsrvol; bsrvol=bsrvol->next) {
873 vol = new_restore_volume();
874 bstrncpy(vol->VolumeName, bsrvol->VolumeName, sizeof(vol->VolumeName));
875 bstrncpy(vol->MediaType, bsrvol->MediaType, sizeof(vol->MediaType));
876 bstrncpy(vol->device, bsrvol->device, sizeof(vol->device));
877 vol->Slot = bsrvol->Slot;
878 vol->start_file = sfile;
879 if (AddRestoreVolume(jcr, vol)) {
880 jcr->NumReadVolumes++;
881 Dmsg2(400, "Added volume=%s mediatype=%s\n", vol->VolumeName,
882 vol->MediaType);
883 } else {
884 Dmsg1(400, "Duplicate volume %s\n", vol->VolumeName);
885 free((char *)vol);
886 }
887 sfile = 0; /* start at beginning of second volume */
888 }
889 }
890 } else {
891 /* This is the old way -- deprecated */
892 for (p = jcr->dcr->VolumeName; p && *p; ) {
893 n = strchr(p, '|'); /* volume name separator */
894 if (n) {
895 *n++ = 0; /* Terminate name */
896 }
897 vol = new_restore_volume();
898 bstrncpy(vol->VolumeName, p, sizeof(vol->VolumeName));
899 bstrncpy(vol->MediaType, jcr->dcr->media_type, sizeof(vol->MediaType));
900 if (AddRestoreVolume(jcr, vol)) {
901 jcr->NumReadVolumes++;
902 } else {
903 free((char *)vol);
904 }
905 p = n;
906 }
907 }
908 }
909
FreeRestoreVolumeList(JobControlRecord * jcr)910 void FreeRestoreVolumeList(JobControlRecord *jcr)
911 {
912 VolumeList *vol = jcr->VolList;
913 VolumeList *tmp;
914
915 for ( ; vol; ) {
916 tmp = vol->next;
917 RemoveReadVolume(jcr, vol->VolumeName);
918 free(vol);
919 vol = tmp;
920 }
921 jcr->VolList = NULL;
922 }
923
924 } /* namespace storagedaemon */
925