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