]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/record.c
e20b4ccd54877d7a07769bc11d70ebb875442069
[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 John Walker.
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    rec->Block = block->BlockNumber;
448    rec->File = ((DEVICE *)block->dev)->file;
449
450    /* Clear state flags */
451    rec->state = 0;
452    if (block->dev->is_tape()) {
453       rec->state |= REC_ISTAPE;
454    }
455
456
457    /*
458     * Get the header. There is always a full header,
459     * otherwise we find it in the next block.
460     */
461    Dmsg3(450, "Block=%d Ver=%d size=%u\n", block->BlockNumber, block->BlockVer,
462          block->block_len);
463    if (block->BlockVer == 1) {
464       rhl = RECHDR1_LENGTH;
465    } else {
466       rhl = RECHDR2_LENGTH;
467    }
468    if (remlen >= rhl) {
469       Dmsg4(450, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n",
470             remlen, rec->data_len, rec->remainder, block->BlockVer);
471
472       unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
473       if (block->BlockVer == 1) {
474          unser_uint32(VolSessionId);
475          unser_uint32(VolSessionTime);
476       } else {
477          VolSessionId = block->VolSessionId;
478          VolSessionTime = block->VolSessionTime;
479       }
480       unser_int32(FileIndex);
481       unser_int32(Stream);
482       unser_uint32(data_bytes);
483
484       block->bufp += rhl;
485       block->binbuf -= rhl;
486       remlen -= rhl;
487
488       /* If we are looking for more (remainder!=0), we reject anything
489        *  where the VolSessionId and VolSessionTime don't agree
490        */
491       if (rec->remainder && (rec->VolSessionId != VolSessionId ||
492                              rec->VolSessionTime != VolSessionTime)) {
493          rec->state |= REC_NO_MATCH;
494          Dmsg0(450, "remainder and VolSession doesn't match\n");
495          return false;             /* This is from some other Session */
496       }
497
498       /* if Stream is negative, it means that this is a continuation
499        * of a previous partially written record.
500        */
501       if (Stream < 0) {               /* continuation record? */
502          Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
503             rec->remainder);
504          rec->state |= REC_CONTINUATION;
505          if (!rec->remainder) {       /* if we didn't read previously */
506             rec->data_len = 0;        /* return data as if no continuation */
507          } else if (rec->Stream != -Stream) {
508             rec->state |= REC_NO_MATCH;
509             return false;             /* This is from some other Session */
510          }
511          rec->Stream = -Stream;       /* set correct Stream */
512       } else {                        /* Regular record */
513          rec->Stream = Stream;
514          rec->data_len = 0;           /* transfer to beginning of data */
515       }
516       rec->VolSessionId = VolSessionId;
517       rec->VolSessionTime = VolSessionTime;
518       rec->FileIndex = FileIndex;
519       if (FileIndex > 0) {
520          if (block->FirstIndex == 0) {
521             block->FirstIndex = FileIndex;
522          }
523          block->LastIndex = FileIndex;
524       }
525
526       Dmsg6(450, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
527                  "remlen=%d data_len=%d\n",
528          FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
529          stream_to_ascii(buf2, rec->Stream, rec->FileIndex), data_bytes, remlen,
530          rec->data_len);
531    } else {
532       /*
533        * No more records in this block because the number
534        * of remaining bytes are less than a record header
535        * length, so return empty handed, but indicate that
536        * he must read again. By returning, we allow the
537        * higher level routine to fetch the next block and
538        * then reread.
539        */
540       Dmsg0(450, "read_record_block: nothing\n");
541       rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
542       empty_block(block);                      /* mark block empty */
543       return false;
544    }
545
546    /* Sanity check */
547    if (data_bytes >= MAX_BLOCK_LENGTH) {
548       /*
549        * Something is wrong, force read of next block, abort 
550        *   continuing with this block.
551        */
552       rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
553       empty_block(block);
554       Jmsg2(dcr->jcr, M_WARNING, 0, _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"),
555          MAX_BLOCK_LENGTH, data_bytes);
556       return false;
557    }
558
559    rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
560
561    /*
562     * At this point, we have read the header, now we
563     * must transfer as much of the data record as
564     * possible taking into account: 1. A partial
565     * data record may have previously been transferred,
566     * 2. The current block may not contain the whole data
567     * record.
568     */
569    if (remlen >= data_bytes) {
570       /* Got whole record */
571       memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
572       block->bufp += data_bytes;
573       block->binbuf -= data_bytes;
574       rec->data_len += data_bytes;
575    } else {
576       /* Partial record */
577       memcpy(rec->data+rec->data_len, block->bufp, remlen);
578       block->bufp += remlen;
579       block->binbuf -= remlen;
580       rec->data_len += remlen;
581       rec->remainder = 1;             /* partial record transferred */
582       Dmsg1(450, "read_record_block: partial xfered=%d\n", rec->data_len);
583       rec->state |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
584       return true;
585    }
586    rec->remainder = 0;
587    Dmsg4(450, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
588       FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
589       stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
590    return true;                       /* transferred full record */
591 }