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