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