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