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