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