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