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