]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/record.c
Tweak SD debug code
[bacula/bacula] / bacula / src / stored / record.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2001-2010 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU 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    General Public License for more details.
17
18    You should have received a copy of the GNU 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    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   record.c -- tape record handling functions
31  *
32  *              Kern Sibbald, April MMI
33  *                added BB02 format October MMII
34  *
35  *   Version $Id$
36  *
37  */
38
39
40 #include "bacula.h"
41 #include "stored.h"
42
43 /*
44  * Convert a FileIndex into a printable
45  *   ASCII string.  Not reentrant.
46  * If the FileIndex is negative, it flags the
47  *   record as a Label, otherwise it is simply
48  *   the FileIndex of the current file.
49  */
50 const char *FI_to_ascii(char *buf, int fi)
51 {
52    if (fi >= 0) {
53       sprintf(buf, "%d", fi);
54       return buf;
55    }
56    switch (fi) {
57    case PRE_LABEL:
58       return "PRE_LABEL";
59    case VOL_LABEL:
60       return "VOL_LABEL";
61    case EOM_LABEL:
62       return "EOM_LABEL";
63    case SOS_LABEL:
64       return "SOS_LABEL";
65    case EOS_LABEL:
66       return "EOS_LABEL";
67    case EOT_LABEL:
68       return "EOT_LABEL";
69       break;
70    case SOB_LABEL:
71       return "SOB_LABEL";
72       break;
73    case EOB_LABEL:
74       return "EOB_LABEL";
75       break;
76    default:
77      sprintf(buf, _("unknown: %d"), fi);
78      return buf;
79    }
80 }
81
82
83 /*
84  * Convert a Stream ID into a printable
85  * ASCII string.  Not reentrant.
86
87  * A negative stream number represents
88  *   stream data that is continued from a
89  *   record in the previous block.
90  * If the FileIndex is negative, we are
91  *   dealing with a Label, hence the
92  *   stream is the JobId.
93  */
94 const char *stream_to_ascii(char *buf, int stream, int fi)
95 {
96     if (fi < 0) {
97        sprintf(buf, "%d", stream);
98        return buf;
99     }
100     switch (stream) {
101     case STREAM_UNIX_ATTRIBUTES:
102        return "UATTR";
103     case STREAM_FILE_DATA:
104        return "DATA";
105     case STREAM_WIN32_DATA:
106        return "WIN32-DATA";
107     case STREAM_WIN32_GZIP_DATA:
108        return "WIN32-GZIP";
109     case STREAM_MD5_DIGEST:
110        return "MD5";
111     case STREAM_SHA1_DIGEST:
112        return "SHA1";
113     case STREAM_GZIP_DATA:
114        return "GZIP";
115     case STREAM_UNIX_ATTRIBUTES_EX:
116        return "UNIX-ATTR-EX";
117     case STREAM_SPARSE_DATA:
118        return "SPARSE-DATA";
119     case STREAM_SPARSE_GZIP_DATA:
120        return "SPARSE-GZIP";
121     case STREAM_PROGRAM_NAMES:
122        return "PROG-NAMES";
123     case STREAM_PROGRAM_DATA:
124        return "PROG-DATA";
125     case STREAM_PLUGIN_NAME:
126        return "PLUGIN-NAME";
127     case STREAM_MACOS_FORK_DATA:
128        return "MACOS-RSRC";
129     case STREAM_HFSPLUS_ATTRIBUTES:
130        return "HFSPLUS-ATTR";
131     case STREAM_SHA256_DIGEST:
132        return "SHA256";
133     case STREAM_SHA512_DIGEST:
134        return "SHA512";
135     case STREAM_SIGNED_DIGEST:
136        return "SIGNED-DIGEST";
137     case STREAM_ENCRYPTED_SESSION_DATA:
138        return "ENCRYPTED-SESSION-DATA";
139     case STREAM_ENCRYPTED_FILE_DATA:
140        return "ENCRYPTED-FILE";
141     case STREAM_ENCRYPTED_FILE_GZIP_DATA:
142        return "ENCRYPTED-GZIP";
143     case STREAM_ENCRYPTED_WIN32_DATA:
144        return "ENCRYPTED-WIN32-DATA";
145     case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
146        return "ENCRYPTED-WIN32-GZIP";
147     case STREAM_ENCRYPTED_MACOS_FORK_DATA:
148        return "ENCRYPTED-MACOS-RSRC";
149     case -STREAM_UNIX_ATTRIBUTES:
150        return "contUATTR";
151     case -STREAM_FILE_DATA:
152        return "contDATA";
153     case -STREAM_WIN32_DATA:
154        return "contWIN32-DATA";
155     case -STREAM_WIN32_GZIP_DATA:
156        return "contWIN32-GZIP";
157     case -STREAM_MD5_DIGEST:
158        return "contMD5";
159     case -STREAM_SHA1_DIGEST:
160        return "contSHA1";
161     case -STREAM_GZIP_DATA:
162        return "contGZIP";
163     case -STREAM_UNIX_ATTRIBUTES_EX:
164        return "contUNIX-ATTR-EX";
165     case -STREAM_SPARSE_DATA:
166        return "contSPARSE-DATA";
167     case -STREAM_SPARSE_GZIP_DATA:
168        return "contSPARSE-GZIP";
169     case -STREAM_PROGRAM_NAMES:
170        return "contPROG-NAMES";
171     case -STREAM_PROGRAM_DATA:
172        return "contPROG-DATA";
173     case -STREAM_MACOS_FORK_DATA:
174        return "contMACOS-RSRC";
175     case -STREAM_HFSPLUS_ATTRIBUTES:
176        return "contHFSPLUS-ATTR";
177     case -STREAM_SHA256_DIGEST:
178        return "contSHA256";
179     case -STREAM_SHA512_DIGEST:
180        return "contSHA512";
181     case -STREAM_SIGNED_DIGEST:
182        return "contSIGNED-DIGEST";
183     case -STREAM_ENCRYPTED_SESSION_DATA:
184        return "contENCRYPTED-SESSION-DATA";
185     case -STREAM_ENCRYPTED_FILE_DATA:
186        return "contENCRYPTED-FILE";
187     case -STREAM_ENCRYPTED_FILE_GZIP_DATA:
188        return "contENCRYPTED-GZIP";
189     case -STREAM_ENCRYPTED_WIN32_DATA:
190        return "contENCRYPTED-WIN32-DATA";
191     case -STREAM_ENCRYPTED_WIN32_GZIP_DATA:
192        return "contENCRYPTED-WIN32-GZIP";
193     case -STREAM_ENCRYPTED_MACOS_FORK_DATA:
194        return "contENCRYPTED-MACOS-RSRC";
195     case -STREAM_PLUGIN_NAME:
196        return "contPLUGIN-NAME";
197
198     default:
199        sprintf(buf, "%d", stream);
200        return buf;
201     }
202 }
203
204 /*
205  * Return a new record entity
206  */
207 DEV_RECORD *new_record(void)
208 {
209    DEV_RECORD *rec;
210
211    rec = (DEV_RECORD *)get_memory(sizeof(DEV_RECORD));
212    memset(rec, 0, sizeof(DEV_RECORD));
213    rec->data = get_pool_memory(PM_MESSAGE);
214    return rec;
215 }
216
217 void empty_record(DEV_RECORD *rec)
218 {
219    rec->File = rec->Block = 0;
220    rec->VolSessionId = rec->VolSessionTime = 0;
221    rec->FileIndex = rec->Stream = 0;
222    rec->data_len = rec->remainder = 0;
223    rec->state &= ~(REC_PARTIAL_RECORD|REC_BLOCK_EMPTY|REC_NO_MATCH|REC_CONTINUATION);
224 }
225
226 /*
227  * Free the record entity
228  *
229  */
230 void free_record(DEV_RECORD *rec)
231 {
232    Dmsg0(950, "Enter free_record.\n");
233    if (rec->data) {
234       free_pool_memory(rec->data);
235    }
236    Dmsg0(950, "Data buf is freed.\n");
237    free_pool_memory((POOLMEM *)rec);
238    Dmsg0(950, "Leave free_record.\n");
239 }
240
241
242 /*
243  * Write a Record to the block
244  *
245  *  Returns: false on failure (none or partially written)
246  *           true  on success (all bytes written)
247  *
248  *  and remainder returned in packet.
249  *
250  *  We require enough room for the header, and we deal with
251  *  two special cases. 1. Only part of the record may have
252  *  been transferred the last time (when remainder is
253  *  non-zero), and 2. The remaining bytes to write may not
254  *  all fit into the block.
255  */
256 bool write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
257 {
258    ser_declare;
259    uint32_t remlen;
260    char buf1[100], buf2[100];
261
262    remlen = block->buf_len - block->binbuf;
263
264    ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
265    ASSERT(block->buf_len >= block->binbuf);
266
267    Dmsg6(890, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n"
268       "rem=%d remainder=%d\n",
269       FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
270       stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
271       remlen, rec->remainder);
272
273    /*
274     * If rec->remainder is non-zero, we have been called a
275     *  second (or subsequent) time to finish writing a record
276     *  that did not previously fit into the block.
277     */
278    if (rec->remainder == 0) {
279       /* Require enough room to write a full header */
280       if (remlen >= WRITE_RECHDR_LENGTH) {
281          ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
282          if (BLOCK_VER == 1) {
283             ser_uint32(rec->VolSessionId);
284             ser_uint32(rec->VolSessionTime);
285          } else {
286             block->VolSessionId = rec->VolSessionId;
287             block->VolSessionTime = rec->VolSessionTime;
288          }
289          ser_int32(rec->FileIndex);
290          ser_int32(rec->Stream);
291          ser_uint32(rec->data_len);
292
293          block->bufp += WRITE_RECHDR_LENGTH;
294          block->binbuf += WRITE_RECHDR_LENGTH;
295          remlen -= WRITE_RECHDR_LENGTH;
296          rec->remainder = rec->data_len;
297          if (rec->FileIndex > 0) {
298             /* If data record, update what we have in this block */
299             if (block->FirstIndex == 0) {
300                block->FirstIndex = rec->FileIndex;
301             }
302             block->LastIndex = rec->FileIndex;
303          }
304       } else {
305          rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
306          return false;
307       }
308    } else {
309       /*
310        * We are here to write unwritten bytes from a previous
311        * time. Presumably we have a new buffer (possibly
312        * containing a volume label), so the new header
313        * should be able to fit in the block -- otherwise we have
314        * an error.  Note, we have to continue splitting the
315        * data record if it is longer than the block.
316        *
317        * First, write the header, then write as much as
318        * possible of the data record.
319        *
320        * Every time we write a header and it is a continuation
321        * of a previous partially written record, we store the
322        * Stream as -Stream in the record header.
323        */
324       ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
325       if (BLOCK_VER == 1) {
326          ser_uint32(rec->VolSessionId);
327          ser_uint32(rec->VolSessionTime);
328       } else {
329          block->VolSessionId = rec->VolSessionId;
330          block->VolSessionTime = rec->VolSessionTime;
331       }
332       ser_int32(rec->FileIndex);
333       if (rec->remainder > rec->data_len) {
334          ser_int32(rec->Stream);      /* normal full header */
335          ser_uint32(rec->data_len);
336          rec->remainder = rec->data_len; /* must still do data record */
337       } else {
338          ser_int32(-rec->Stream);     /* mark this as a continuation record */
339          ser_uint32(rec->remainder);  /* bytes to do */
340       }
341
342       /* Require enough room to write a full header */
343       ASSERT(remlen >= WRITE_RECHDR_LENGTH);
344
345       block->bufp += WRITE_RECHDR_LENGTH;
346       block->binbuf += WRITE_RECHDR_LENGTH;
347       remlen -= WRITE_RECHDR_LENGTH;
348       if (rec->FileIndex > 0) {
349          /* If data record, update what we have in this block */
350          if (block->FirstIndex == 0) {
351             block->FirstIndex = rec->FileIndex;
352          }
353          block->LastIndex = rec->FileIndex;
354       }
355    }
356    if (remlen == 0) {
357       return false;                   /* partial transfer */
358    }
359
360    /*
361     * Now deal with data record.
362     * Part of it may have already been transferred, and we
363     * may not have enough room to transfer the whole this time.
364     */
365    if (rec->remainder > 0) {
366       /* Write as much of data as possible */
367       if (remlen >= rec->remainder) {
368          memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
369                 rec->remainder);
370          block->bufp += rec->remainder;
371          block->binbuf += rec->remainder;
372       } else {
373          memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
374                 remlen);
375 #ifdef xxxxxSMCHECK
376          if (!sm_check_rtn(__FILE__, __LINE__, False)) {
377             /* We damaged a buffer */
378             Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n"
379                "rem=%d remainder=%d\n",
380                FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
381                stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
382                remlen, rec->remainder);
383             Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
384                block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
385                remlen);
386             Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
387                block->buf, block->bufp-block->buf);
388
389                Emsg0(M_ABORT, 0, _("Damaged buffer\n"));
390          }
391 #endif
392
393          block->bufp += remlen;
394          block->binbuf += remlen;
395          rec->remainder -= remlen;
396          return false;                /* did partial transfer */
397       }
398    }
399    rec->remainder = 0;                /* did whole transfer */
400    return true;
401 }
402
403
404 /*
405  * Test if we can write whole record to the block
406  *
407  *  Returns: false on failure
408  *           true  on success (all bytes can be written)
409  */
410 bool can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
411 {
412    uint32_t remlen;
413
414    remlen = block->buf_len - block->binbuf;
415    if (rec->remainder == 0) {
416       if (remlen >= WRITE_RECHDR_LENGTH) {
417          remlen -= WRITE_RECHDR_LENGTH;
418          rec->remainder = rec->data_len;
419       } else {
420          return false;
421       }
422    } else {
423       return false;
424    }
425    if (rec->remainder > 0 && remlen < rec->remainder) {
426       return false;
427    }
428    return true;
429 }
430
431 uint64_t get_record_address(DEV_RECORD *rec)
432 {
433    return ((uint64_t)rec->File)<<32 | rec->Block;
434 }
435
436 /*
437  * Read a Record from the block
438  *  Returns: false if nothing read or if the continuation record does not match.
439  *                 In both of these cases, a block read must be done.
440  *           true  if at least the record header was read, this
441  *                 routine may have to be called again with a new
442  *                 block if the entire record was not read.
443  */
444 bool read_record_from_block(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
445 {
446    ser_declare;
447    uint32_t remlen;
448    uint32_t VolSessionId;
449    uint32_t VolSessionTime;
450    int32_t  FileIndex;
451    int32_t  Stream;
452    uint32_t data_bytes;
453    uint32_t rhl;
454    char buf1[100], buf2[100];
455
456    remlen = block->binbuf;
457
458    /* Clear state flags */
459    rec->state = 0;
460    if (block->dev->is_tape()) {
461       rec->state |= REC_ISTAPE;
462    }
463    rec->Block = ((DEVICE *)block->dev)->EndBlock;
464    rec->File = ((DEVICE *)block->dev)->EndFile;
465
466    /*
467     * Get the header. There is always a full header,
468     * otherwise we find it in the next block.
469     */
470    Dmsg3(450, "Block=%d Ver=%d size=%u\n", block->BlockNumber, block->BlockVer,
471          block->block_len);
472    if (block->BlockVer == 1) {
473       rhl = RECHDR1_LENGTH;
474    } else {
475       rhl = RECHDR2_LENGTH;
476    }
477    if (remlen >= rhl) {
478       Dmsg4(450, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n",
479             remlen, rec->data_len, rec->remainder, block->BlockVer);
480
481       unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
482       if (block->BlockVer == 1) {
483          unser_uint32(VolSessionId);
484          unser_uint32(VolSessionTime);
485       } else {
486          VolSessionId = block->VolSessionId;
487          VolSessionTime = block->VolSessionTime;
488       }
489       unser_int32(FileIndex);
490       unser_int32(Stream);
491       unser_uint32(data_bytes);
492
493       block->bufp += rhl;
494       block->binbuf -= rhl;
495       remlen -= rhl;
496
497       /* If we are looking for more (remainder!=0), we reject anything
498        *  where the VolSessionId and VolSessionTime don't agree
499        */
500       if (rec->remainder && (rec->VolSessionId != VolSessionId ||
501                              rec->VolSessionTime != VolSessionTime)) {
502          rec->state |= REC_NO_MATCH;
503          Dmsg0(450, "remainder and VolSession doesn't match\n");
504          return false;             /* This is from some other Session */
505       }
506
507       /* if Stream is negative, it means that this is a continuation
508        * of a previous partially written record.
509        */
510       if (Stream < 0) {               /* continuation record? */
511          Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
512             rec->remainder);
513          rec->state |= REC_CONTINUATION;
514          if (!rec->remainder) {       /* if we didn't read previously */
515             rec->data_len = 0;        /* return data as if no continuation */
516          } else if (rec->Stream != -Stream) {
517             rec->state |= REC_NO_MATCH;
518             return false;             /* This is from some other Session */
519          }
520          rec->Stream = -Stream;       /* set correct Stream */
521       } else {                        /* Regular record */
522          rec->Stream = Stream;
523          rec->data_len = 0;           /* transfer to beginning of data */
524       }
525       rec->VolSessionId = VolSessionId;
526       rec->VolSessionTime = VolSessionTime;
527       rec->FileIndex = FileIndex;
528       if (FileIndex > 0) {
529          if (block->FirstIndex == 0) {
530             block->FirstIndex = FileIndex;
531          }
532          block->LastIndex = FileIndex;
533       }
534
535       Dmsg6(450, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
536                  "remlen=%d data_len=%d\n",
537          FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
538          stream_to_ascii(buf2, rec->Stream, rec->FileIndex), data_bytes, remlen,
539          rec->data_len);
540    } else {
541       /*
542        * No more records in this block because the number
543        * of remaining bytes are less than a record header
544        * length, so return empty handed, but indicate that
545        * he must read again. By returning, we allow the
546        * higher level routine to fetch the next block and
547        * then reread.
548        */
549       Dmsg0(450, "read_record_block: nothing\n");
550       rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
551       empty_block(block);                      /* mark block empty */
552       return false;
553    }
554
555    /* Sanity check */
556    if (data_bytes >= MAX_BLOCK_LENGTH) {
557       /*
558        * Something is wrong, force read of next block, abort 
559        *   continuing with this block.
560        */
561       rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
562       empty_block(block);
563       Jmsg2(dcr->jcr, M_WARNING, 0, _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"),
564          MAX_BLOCK_LENGTH, data_bytes);
565       return false;
566    }
567
568    rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
569
570    /*
571     * At this point, we have read the header, now we
572     * must transfer as much of the data record as
573     * possible taking into account: 1. A partial
574     * data record may have previously been transferred,
575     * 2. The current block may not contain the whole data
576     * record.
577     */
578    if (remlen >= data_bytes) {
579       /* Got whole record */
580       memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
581       block->bufp += data_bytes;
582       block->binbuf -= data_bytes;
583       rec->data_len += data_bytes;
584    } else {
585       /* Partial record */
586       memcpy(rec->data+rec->data_len, block->bufp, remlen);
587       block->bufp += remlen;
588       block->binbuf -= remlen;
589       rec->data_len += remlen;
590       rec->remainder = 1;             /* partial record transferred */
591       Dmsg1(450, "read_record_block: partial xfered=%d\n", rec->data_len);
592       rec->state |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
593       return true;
594    }
595    rec->remainder = 0;
596    Dmsg4(450, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
597       FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
598       stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
599    return true;                       /* transferred full record */
600 }