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