1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2002-2010 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2012 Planets Communications B.V.
6 Copyright (C) 2013-2016 Bareos GmbH & Co. KG
7
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
11 in the file LICENSE.
12
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 Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero 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
21 02110-1301, USA.
22 */
23 /*
24 * Kern E. Sibbald, August MMII
25 */
26 /**
27 * This file provides routines that will handle all
28 * the gory little details of reading a record from a Bareos
29 * archive. It uses a callback to pass you each record in turn,
30 * as well as a callback for mounting the next tape. It takes
31 * care of reading blocks, applying the bsr, ...
32 *
33 * Note, this routine is really the heart of the restore routines,
34 * and we are *really* bit pushing here so be careful about making
35 * any modifications.
36 */
37
38 #include "include/bareos.h"
39 #include "stored/stored.h"
40 #include "stored/butil.h"
41 #include "stored/device.h"
42 #include "stored/label.h"
43 #include "stored/match_bsr.h"
44 #include "include/jcr.h"
45
46 namespace storagedaemon {
47
48 static const int debuglevel = 500;
49
HandleSessionRecord(Device * dev,DeviceRecord * rec,SESSION_LABEL * sessrec)50 static void HandleSessionRecord(Device *dev, DeviceRecord *rec, SESSION_LABEL *sessrec)
51 {
52 const char *rtype;
53 char buf[100];
54
55 memset(sessrec, 0, sizeof(SESSION_LABEL));
56 switch (rec->FileIndex) {
57 case PRE_LABEL:
58 rtype = _("Fresh Volume Label");
59 break;
60 case VOL_LABEL:
61 rtype = _("Volume Label");
62 UnserVolumeLabel(dev, rec);
63 break;
64 case SOS_LABEL:
65 rtype = _("Begin Session");
66 UnserSessionLabel(sessrec, rec);
67 break;
68 case EOS_LABEL:
69 rtype = _("End Session");
70 break;
71 case EOM_LABEL:
72 rtype = _("End of Media");
73 break;
74 default:
75 Bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
76 rtype = buf;
77 break;
78 }
79 Dmsg5(debuglevel, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
80 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
81 }
82
rec_state_bits_to_str(DeviceRecord * rec)83 static char *rec_state_bits_to_str(DeviceRecord *rec)
84 {
85 static char buf[200];
86
87 buf[0] = 0;
88 if (BitIsSet(REC_NO_HEADER, rec->state_bits)) {
89 bstrncat(buf, _("Nohdr,"), sizeof(buf));
90 }
91
92 if (IsPartialRecord(rec)) {
93 bstrncat(buf, _("partial,"), sizeof(buf));
94 }
95
96 if (BitIsSet(REC_BLOCK_EMPTY, rec->state_bits)) {
97 bstrncat(buf, _("empty,"), sizeof(buf));
98 }
99
100 if (BitIsSet(REC_NO_MATCH, rec->state_bits)) {
101 bstrncat(buf, _("Nomatch,"), sizeof(buf));
102 }
103
104 if (BitIsSet(REC_CONTINUATION, rec->state_bits)) {
105 bstrncat(buf, _("cont,"), sizeof(buf));
106 }
107
108 if (buf[0]) {
109 buf[strlen(buf)-1] = 0;
110 }
111
112 return buf;
113 }
114
115 /**
116 * Allocate a new read context which will contains accumulated data from a read session.
117 */
new_read_context(void)118 READ_CTX *new_read_context(void)
119 {
120 DeviceRecord *rec = NULL;
121 READ_CTX *rctx;
122
123 rctx = (READ_CTX *)malloc(sizeof(READ_CTX));
124 memset(rctx, 0, sizeof(READ_CTX));
125
126 rctx->recs = New(dlist(rec, &rec->link));
127 return rctx;
128 }
129
130 /**
131 * Free a read context which contains accumulated data from a read session.
132 */
FreeReadContext(READ_CTX * rctx)133 void FreeReadContext(READ_CTX *rctx)
134 {
135 DeviceRecord *rec;
136
137 /*
138 * Walk down list and free all remaining allocated recs
139 */
140 while (!rctx->recs->empty()) {
141 rec = (DeviceRecord *)rctx->recs->first();
142 rctx->recs->remove(rec);
143 FreeRecord(rec);
144 }
145 delete rctx->recs;
146
147 free(rctx);
148 }
149
150 /**
151 * Setup the record pointer in the Read Context.
152 * Reuse an already existing record when available in the linked
153 * list or allocate a fresh one and prepend it in the linked list.
154 */
ReadContextSetRecord(DeviceControlRecord * dcr,READ_CTX * rctx)155 void ReadContextSetRecord(DeviceControlRecord *dcr, READ_CTX *rctx)
156 {
157 DeviceRecord *rec;
158 bool found = false;
159
160 foreach_dlist(rec, rctx->recs) {
161 if (rec->VolSessionId == dcr->block->VolSessionId &&
162 rec->VolSessionTime == dcr->block->VolSessionTime) {
163 found = true;
164 break;
165 }
166 }
167
168 if (!found) {
169 rec = new_record();
170 rctx->recs->prepend(rec);
171 Dmsg3(debuglevel, "New record for state=%s SI=%d ST=%d\n",
172 rec_state_bits_to_str(rec),
173 dcr->block->VolSessionId,
174 dcr->block->VolSessionTime);
175 }
176
177 rctx->rec = rec;
178 }
179
180 /**
181 * Read the next block from the device and handle any volume
182 * switches etc.
183 *
184 * Returns: true on success
185 * false on error
186 *
187 * Any fatal error sets the status bool to false.
188 */
ReadNextBlockFromDevice(DeviceControlRecord * dcr,SESSION_LABEL * sessrec,bool RecordCb (DeviceControlRecord * dcr,DeviceRecord * rec),bool mount_cb (DeviceControlRecord * dcr),bool * status)189 bool ReadNextBlockFromDevice(DeviceControlRecord *dcr,
190 SESSION_LABEL *sessrec,
191 bool RecordCb(DeviceControlRecord *dcr, DeviceRecord *rec),
192 bool mount_cb(DeviceControlRecord *dcr),
193 bool *status)
194 {
195 JobControlRecord *jcr = dcr->jcr;
196 DeviceRecord *trec;
197
198 while (1) {
199 switch(dcr->ReadBlockFromDevice(CHECK_BLOCK_NUMBERS)) {
200 case DeviceControlRecord::ReadStatus::Ok:
201 // no handling required if read was successful
202 break;
203 case DeviceControlRecord::ReadStatus::EndOfTape:
204 Jmsg(jcr, M_INFO, 0, _("End of Volume at file %u on device %s, Volume \"%s\"\n"),
205 dcr->dev->file, dcr->dev->print_name(), dcr->VolumeName);
206
207 VolumeUnused(dcr); /* mark volume unused */
208 if (!mount_cb(dcr)) {
209 Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
210 if (RecordCb) {
211 /*
212 * Create EOT Label so that Media record may
213 * be properly updated because this is the last
214 * tape.
215 */
216 trec = new_record();
217 trec->FileIndex = EOT_LABEL;
218 trec->File = dcr->dev->file;
219 *status = RecordCb(dcr, trec);
220 if (jcr->mount_next_volume) {
221 jcr->mount_next_volume = false;
222 dcr->dev->ClearEot();
223 }
224 FreeRecord(trec);
225 }
226 return false;
227 }
228 jcr->mount_next_volume = false;
229
230 /*
231 * We just have a new tape up, now read the label (first record)
232 * and pass it off to the callback routine, then continue
233 * most likely reading the previous record.
234 */
235 dcr->ReadBlockFromDevice(NO_BLOCK_NUMBER_CHECK);
236 trec = new_record();
237 ReadRecordFromBlock(dcr, trec);
238 HandleSessionRecord(dcr->dev, trec, sessrec);
239 if (RecordCb) {
240 RecordCb(dcr, trec);
241 }
242
243 FreeRecord(trec);
244 PositionDeviceToFirstFile(jcr, dcr);
245
246 /*
247 * After reading label, we must read first data block
248 */
249 continue;
250 case DeviceControlRecord::ReadStatus::EndOfFile:
251 Dmsg3(200, "End of file %u on device %s, Volume \"%s\"\n",
252 dcr->dev->file, dcr->dev->print_name(), dcr->VolumeName);
253 continue;
254 default:
255 if (dcr->dev->IsShortBlock()) {
256 Jmsg1(jcr, M_ERROR, 0, "%s", dcr->dev->errmsg);
257 continue;
258 } else {
259 /*
260 * I/O error or strange end of tape
261 */
262 DisplayTapeErrorStatus(jcr, dcr->dev);
263 if (forge_on || jcr->ignore_label_errors) {
264 dcr->dev->fsr(1); /* try skipping bad record */
265 Pmsg0(000, _("Did fsr in attemp to skip bad record.\n"));
266 continue;
267 }
268 *status = false;
269 return false;
270 }
271 }
272
273 Dmsg2(debuglevel, "Read new block at pos=%u:%u\n", dcr->dev->file, dcr->dev->block_num);
274 return true;
275 }
276 }
277
278 /**
279 * Read the next record from a block.
280 *
281 * Returns: true on continue processing.
282 * false on error or when done with this block.
283 *
284 * When we are done processing all records the done bool is set to true.
285 */
ReadNextRecordFromBlock(DeviceControlRecord * dcr,READ_CTX * rctx,bool * done)286 bool ReadNextRecordFromBlock(DeviceControlRecord *dcr, READ_CTX *rctx, bool *done)
287 {
288 JobControlRecord *jcr = dcr->jcr;
289 Device *dev = dcr->dev;
290 DeviceBlock *block = dcr->block;
291 DeviceRecord *rec = rctx->rec;
292
293 while (1) {
294 if (!ReadRecordFromBlock(dcr, rec)) {
295 Dmsg3(400, "!read-break. state_bits=%s blk=%d rem=%d\n",
296 rec_state_bits_to_str(rec), block->BlockNumber, rec->remainder);
297 return false;
298 }
299
300 Dmsg5(debuglevel, "read-OK. state_bits=%s blk=%d rem=%d file:block=%u:%u\n",
301 rec_state_bits_to_str(rec), block->BlockNumber, rec->remainder,
302 dev->file, dev->block_num);
303
304 /*
305 * At this point, we have at least a record header.
306 * Now decide if we want this record or not, but remember
307 * before accessing the record, we may need to read again to
308 * get all the data.
309 */
310 rctx->records_processed++;
311 Dmsg6(debuglevel, "recno=%d state_bits=%s blk=%d SI=%d ST=%d FI=%d\n",
312 rctx->records_processed, rec_state_bits_to_str(rec), block->BlockNumber,
313 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
314
315 if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
316 Dmsg0(40, "Get EOM LABEL\n");
317 return false;
318 }
319
320 /*
321 * Some sort of label?
322 */
323 if (rec->FileIndex < 0) {
324 HandleSessionRecord(dcr->dev, rec, &rctx->sessrec);
325 if (jcr->bsr) {
326 /*
327 * We just check block FI and FT not FileIndex
328 */
329 rec->match_stat = MatchBsrBlock(jcr->bsr, dcr->block);
330 } else {
331 rec->match_stat = 0;
332 }
333
334 return true;
335 }
336
337 /*
338 * Apply BootStrapRecord filter
339 */
340 if (jcr->bsr) {
341 rec->match_stat = MatchBsr(jcr->bsr, rec, &dev->VolHdr, &rctx->sessrec, jcr);
342 if (rec->match_stat == -1) { /* no more possible matches */
343 *done = true; /* all items found, stop */
344 Dmsg2(debuglevel, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
345 return false;
346 } else if (rec->match_stat == 0) { /* no match */
347 Dmsg4(debuglevel, "BootStrapRecord no match: clear rem=%d FI=%d before SetEof pos %u:%u\n",
348 rec->remainder, rec->FileIndex, dev->file, dev->block_num);
349 rec->remainder = 0;
350 ClearBit(REC_PARTIAL_RECORD, rec->state_bits);
351 if (TryDeviceRepositioning(jcr, rec, dcr)) {
352 return false;
353 }
354 continue; /* we don't want record, read next one */
355 }
356 }
357
358 dcr->VolLastIndex = rec->FileIndex; /* let caller know where we are */
359
360 if (IsPartialRecord(rec)) {
361 Dmsg6(debuglevel, "Partial, break. recno=%d state_bits=%s blk=%d SI=%d ST=%d FI=%d\n",
362 rctx->records_processed, rec_state_bits_to_str(rec), block->BlockNumber, rec->VolSessionId,
363 rec->VolSessionTime, rec->FileIndex);
364 return false; /* read second part of record */
365 }
366
367 if (rctx->lastFileIndex != READ_NO_FILEINDEX && rctx->lastFileIndex != rec->FileIndex) {
368 if (IsThisBsrDone(jcr->bsr, rec) && TryDeviceRepositioning(jcr, rec, dcr)) {
369 Dmsg2(debuglevel, "This bsr done, break pos %u:%u\n", dev->file, dev->block_num);
370 return false;
371 }
372 Dmsg2(debuglevel, "==== inside LastIndex=%d FileIndex=%d\n", rctx->lastFileIndex, rec->FileIndex);
373 }
374
375 Dmsg2(debuglevel, "==== LastIndex=%d FileIndex=%d\n", rctx->lastFileIndex, rec->FileIndex);
376 rctx->lastFileIndex = rec->FileIndex;
377
378 return true;
379 }
380 }
381
382 /**
383 * This subroutine reads all the records and passes them back to your
384 * callback routine (also mount routine at EOM).
385 *
386 * You must not change any values in the DeviceRecord packet
387 */
ReadRecords(DeviceControlRecord * dcr,bool RecordCb (DeviceControlRecord * dcr,DeviceRecord * rec),bool mount_cb (DeviceControlRecord * dcr))388 bool ReadRecords(DeviceControlRecord *dcr,
389 bool RecordCb(DeviceControlRecord *dcr, DeviceRecord *rec),
390 bool mount_cb(DeviceControlRecord *dcr))
391 {
392 JobControlRecord *jcr = dcr->jcr;
393 READ_CTX *rctx;
394 bool ok = true;
395 bool done = false;
396
397 rctx = new_read_context();
398 PositionDeviceToFirstFile(jcr, dcr);
399 jcr->mount_next_volume = false;
400
401 while (ok && !done) {
402 if (JobCanceled(jcr)) {
403 ok = false;
404 break;
405 }
406
407 /*
408 * Read the next block into our buffers.
409 */
410 if (!ReadNextBlockFromDevice(dcr, &rctx->sessrec, RecordCb, mount_cb, &ok)) {
411 break;
412 }
413
414 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
415 /*
416 * This does not stop when file/block are too big
417 */
418 if (!MatchBsrBlock(jcr->bsr, block)) {
419 if (TryDeviceRepositioning(jcr, rctx->rec, dcr)) {
420 break; /* get next volume */
421 }
422 continue; /* skip this record */
423 }
424 #endif
425
426 /*
427 * Get a new record for each Job as defined by VolSessionId and VolSessionTime
428 */
429 if (!rctx->rec ||
430 rctx->rec->VolSessionId != dcr->block->VolSessionId ||
431 rctx->rec->VolSessionTime != dcr->block->VolSessionTime) {
432 ReadContextSetRecord(dcr, rctx);
433 }
434
435 Dmsg3(debuglevel, "Before read rec loop. stat=%s blk=%d rem=%d\n",
436 rec_state_bits_to_str(rctx->rec), dcr->block->BlockNumber, rctx->rec->remainder);
437
438 rctx->records_processed = 0;
439 ClearAllBits(REC_STATE_MAX, rctx->rec->state_bits);
440 rctx->lastFileIndex = READ_NO_FILEINDEX;
441 Dmsg1(debuglevel, "Block %s empty\n", IsBlockEmpty(rctx->rec) ? "is" : "NOT");
442
443 /*
444 * Process the block and read all records in the block and send
445 * them to the defined callback.
446 */
447 while (ok && !IsBlockEmpty(rctx->rec)) {
448 if (!ReadNextRecordFromBlock(dcr, rctx, &done)) {
449 break;
450 }
451
452 if (rctx->rec->FileIndex < 0) {
453 /*
454 * Note, we pass *all* labels to the callback routine. If
455 * he wants to know if they matched the bsr, then he must
456 * check the match_stat in the record */
457 ok = RecordCb(dcr, rctx->rec);
458 } else {
459 DeviceRecord *rec;
460
461 Dmsg6(debuglevel, "OK callback. recno=%d state_bits=%s blk=%d SI=%d ST=%d FI=%d\n",
462 rctx->records_processed, rec_state_bits_to_str(rctx->rec), dcr->block->BlockNumber,
463 rctx->rec->VolSessionId, rctx->rec->VolSessionTime, rctx->rec->FileIndex);
464
465 /*
466 * Perform record translations.
467 */
468 dcr->before_rec = rctx->rec;
469 dcr->after_rec = NULL;
470
471 /*
472 * We want the plugins to be called in reverse order so we give the GeneratePluginEvent()
473 * the reverse argument so it knows that we want the plugins to be called in that order.
474 */
475 if (GeneratePluginEvent(jcr, bsdEventReadRecordTranslation, dcr, true) != bRC_OK) {
476 ok = false;
477 continue;
478 }
479
480 /*
481 * The record got translated when we got an after_rec pointer after calling the
482 * bsdEventReadRecordTranslation plugin event. If no translation has taken place
483 * we just point the rec pointer to same DeviceRecord as in the before_rec pointer.
484 */
485 rec = (dcr->after_rec) ? dcr->after_rec : dcr->before_rec;
486 ok = RecordCb(dcr, rec);
487
488 #if 0
489 /*
490 * If we have a digest stream, we check to see if we have
491 * finished the current bsr, and if so, repositioning will
492 * be turned on.
493 */
494 if (CryptoDigestStreamType(rec->Stream) != CRYPTO_DIGEST_NONE) {
495 Dmsg3(debuglevel, "=== Have digest FI=%u before bsr check pos %u:%u\n",
496 rec->FileIndex, dev->file, dev->block_num);
497 if (IsThisBsrDone(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
498 Dmsg1(debuglevel, "==== BootStrapRecord done at FI=%d\n", rec->FileIndex);
499 Dmsg2(debuglevel, "This bsr done, break pos %u:%u\n",
500 dev->file, dev->block_num);
501 break;
502 }
503 Dmsg2(900, "After is_bsr_done pos %u:%u\n", dev->file, dev->block_num);
504 }
505 #endif
506
507 /*
508 * We can just release the translated record here as the record may not be
509 * changed by the record callback so any changes made don't need to be copied
510 * back to the original DeviceRecord.
511 */
512 if (dcr->after_rec) {
513 FreeRecord(dcr->after_rec);
514 dcr->after_rec = NULL;
515 }
516 }
517 }
518 Dmsg2(debuglevel, "After end recs in block. pos=%u:%u\n", dcr->dev->file, dcr->dev->block_num);
519 }
520 // Dmsg2(debuglevel, "Position=(file:block) %u:%u\n", dcr->dev->file, dcr->dev->block_num);
521
522 FreeReadContext(rctx);
523 PrintBlockReadErrors(jcr, dcr->block);
524
525 return ok;
526 }
527
528 } /* namespace storagedaemon */
529