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