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