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