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