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