]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/record.c
45329a8444d914d635bb4ee844218c56cd2b3a93
[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(int fi)
40 {
41    static char buf[20];
42    if (fi >= 0) {
43       sprintf(buf, "%d", fi);
44       return buf;
45    }
46    switch (fi) {
47    case PRE_LABEL:
48       return "PRE_LABEL";
49    case VOL_LABEL:
50       return "VOL_LABEL";
51    case EOM_LABEL:
52       return "EOM_LABEL";
53    case SOS_LABEL:
54       return "SOS_LABEL";
55    case EOS_LABEL:
56       return "EOS_LABEL";
57    case EOT_LABEL:
58       return "EOT_LABEL";
59       break;
60    default:
61      sprintf(buf, _("unknown: %d"), fi);
62      return buf;
63    }
64 }
65
66
67 /*
68  * Convert a Stream ID into a printable
69  * ASCII string.  Not reentrant.
70
71  * A negative stream number represents
72  *   stream data that is continued from a
73  *   record in the previous block.
74  * If the FileIndex is negative, we are
75  *   dealing with a Label, hence the
76  *   stream is the JobId.
77  */
78 const char *stream_to_ascii(int stream, int fi)
79 {
80     static char buf[20];
81     if (fi < 0) {
82        sprintf(buf, "%d", stream);
83        return buf;
84     }
85     switch (stream) {
86     case STREAM_UNIX_ATTRIBUTES:
87        return "UATTR";
88     case STREAM_FILE_DATA:
89        return "DATA";
90     case STREAM_WIN32_DATA:
91        return "WIN32-DATA";
92     case STREAM_WIN32_GZIP_DATA:
93        return "WIN32-GZIP";
94     case STREAM_MD5_SIGNATURE:
95        return "MD5";
96     case STREAM_SHA1_SIGNATURE:
97        return "SHA1";
98     case STREAM_GZIP_DATA:
99        return "GZIP";
100     case STREAM_UNIX_ATTRIBUTES_EX:
101        return "UNIX-ATTR-EX";
102     case STREAM_SPARSE_DATA:
103        return "SPARSE-DATA";
104     case STREAM_SPARSE_GZIP_DATA:
105        return "SPARSE-GZIP";
106     case STREAM_PROGRAM_NAMES:
107        return "PROG-NAMES";
108     case STREAM_PROGRAM_DATA:
109        return "PROG-DATA";
110     case STREAM_MACOS_FORK_DATA:
111        return "MACOS-RSRC";
112     case STREAM_HFSPLUS_ATTRIBUTES:
113        return "HFSPLUS-ATTR";
114     case -STREAM_UNIX_ATTRIBUTES:
115        return "contUATTR";
116     case -STREAM_FILE_DATA:
117        return "contDATA";
118     case -STREAM_WIN32_DATA:
119        return "contWIN32-DATA";
120     case -STREAM_WIN32_GZIP_DATA:
121        return "contWIN32-GZIP";
122     case -STREAM_MD5_SIGNATURE:
123        return "contMD5";
124     case -STREAM_SHA1_SIGNATURE:
125        return "contSHA1";
126     case -STREAM_GZIP_DATA:
127        return "contGZIP";
128     case -STREAM_UNIX_ATTRIBUTES_EX:
129        return "contUNIX-ATTR-EX";
130     case -STREAM_SPARSE_DATA:
131        return "contSPARSE-DATA";
132     case -STREAM_SPARSE_GZIP_DATA:
133        return "contSPARSE-GZIP";
134     case -STREAM_PROGRAM_NAMES:
135        return "contPROG-NAMES";
136     case -STREAM_PROGRAM_DATA:
137        return "contPROG-DATA";
138     case -STREAM_MACOS_FORK_DATA:
139        return "contMACOS-RSRC";
140     case -STREAM_HFSPLUS_ATTRIBUTES:
141        return "contHFSPLUS-ATTR";
142     default:
143        sprintf(buf, "%d", stream);
144        return buf;
145     }
146 }
147
148 /*
149  * Return a new record entity
150  */
151 DEV_RECORD *new_record(void)
152 {
153    DEV_RECORD *rec;
154
155    rec = (DEV_RECORD *)get_memory(sizeof(DEV_RECORD));
156    memset(rec, 0, sizeof(DEV_RECORD));
157    rec->data = get_pool_memory(PM_MESSAGE);
158    return rec;
159 }
160
161 void empty_record(DEV_RECORD *rec)
162 {
163    rec->File = rec->Block = 0;
164    rec->VolSessionId = rec->VolSessionTime = 0;
165    rec->FileIndex = rec->Stream = 0;
166    rec->data_len = rec->remainder = 0;
167    rec->state &= ~(REC_PARTIAL_RECORD|REC_BLOCK_EMPTY|REC_NO_MATCH|REC_CONTINUATION);
168 }
169
170 /*
171  * Free the record entity
172  *
173  */
174 void free_record(DEV_RECORD *rec)
175 {
176    Dmsg0(950, "Enter free_record.\n");
177    if (rec->data) {
178       free_pool_memory(rec->data);
179    }
180    Dmsg0(950, "Data buf is freed.\n");
181    free_pool_memory((POOLMEM *)rec);
182    Dmsg0(950, "Leave free_record.\n");
183 }
184
185
186 /*
187  * Write a Record to the block
188  *
189  *  Returns: false on failure (none or partially written)
190  *           true  on success (all bytes written)
191  *
192  *  and remainder returned in packet.
193  *
194  *  We require enough room for the header, and we deal with
195  *  two special cases. 1. Only part of the record may have
196  *  been transferred the last time (when remainder is
197  *  non-zero), and 2. The remaining bytes to write may not
198  *  all fit into the block.
199  */
200 bool write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
201 {
202    ser_declare;
203    uint32_t remlen;
204
205    remlen = block->buf_len - block->binbuf;
206
207    ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
208    ASSERT(block->buf_len >= block->binbuf);
209
210    Dmsg6(890, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n"
211 "rem=%d remainder=%d\n",
212       FI_to_ascii(rec->FileIndex), rec->VolSessionId,
213       stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len,
214       remlen, rec->remainder);
215
216    /*
217     * If rec->remainder is non-zero, we have been called a
218     *  second (or subsequent) time to finish writing a record
219     *  that did not previously fit into the block.
220     */
221    if (rec->remainder == 0) {
222       /* Require enough room to write a full header */
223       if (remlen >= WRITE_RECHDR_LENGTH) {
224          ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
225          if (BLOCK_VER == 1) {
226             ser_uint32(rec->VolSessionId);
227             ser_uint32(rec->VolSessionTime);
228          } else {
229             block->VolSessionId = rec->VolSessionId;
230             block->VolSessionTime = rec->VolSessionTime;
231          }
232          ser_int32(rec->FileIndex);
233          ser_int32(rec->Stream);
234          ser_uint32(rec->data_len);
235
236          block->bufp += WRITE_RECHDR_LENGTH;
237          block->binbuf += WRITE_RECHDR_LENGTH;
238          remlen -= WRITE_RECHDR_LENGTH;
239          rec->remainder = rec->data_len;
240          if (rec->FileIndex > 0) {
241             /* If data record, update what we have in this block */
242             if (block->FirstIndex == 0) {
243                block->FirstIndex = rec->FileIndex;
244             }
245             block->LastIndex = rec->FileIndex;
246          }
247       } else {
248          rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
249          return false;
250       }
251    } else {
252       /*
253        * We are here to write unwritten bytes from a previous
254        * time. Presumably we have a new buffer (possibly
255        * containing a volume label), so the new header
256        * should be able to fit in the block -- otherwise we have
257        * an error.  Note, we have to continue splitting the
258        * data record if it is longer than the block.
259        *
260        * First, write the header, then write as much as
261        * possible of the data record.
262        *
263        * Every time we write a header and it is a continuation
264        * of a previous partially written record, we store the
265        * Stream as -Stream in the record header.
266        */
267       ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
268       if (BLOCK_VER == 1) {
269          ser_uint32(rec->VolSessionId);
270          ser_uint32(rec->VolSessionTime);
271       } else {
272          block->VolSessionId = rec->VolSessionId;
273          block->VolSessionTime = rec->VolSessionTime;
274       }
275       ser_int32(rec->FileIndex);
276       if (rec->remainder > rec->data_len) {
277          ser_int32(rec->Stream);      /* normal full header */
278          ser_uint32(rec->data_len);
279          rec->remainder = rec->data_len; /* must still do data record */
280       } else {
281          ser_int32(-rec->Stream);     /* mark this as a continuation record */
282          ser_uint32(rec->remainder);  /* bytes to do */
283       }
284
285       /* Require enough room to write a full header */
286       ASSERT(remlen >= WRITE_RECHDR_LENGTH);
287
288       block->bufp += WRITE_RECHDR_LENGTH;
289       block->binbuf += WRITE_RECHDR_LENGTH;
290       remlen -= WRITE_RECHDR_LENGTH;
291       if (rec->FileIndex > 0) {
292          /* If data record, update what we have in this block */
293          if (block->FirstIndex == 0) {
294             block->FirstIndex = rec->FileIndex;
295          }
296          block->LastIndex = rec->FileIndex;
297       }
298    }
299    if (remlen == 0) {
300       return false;                   /* partial transfer */
301    }
302
303    /*
304     * Now deal with data record.
305     * Part of it may have already been transferred, and we
306     * may not have enough room to transfer the whole this time.
307     */
308    if (rec->remainder > 0) {
309       /* Write as much of data as possible */
310       if (remlen >= rec->remainder) {
311          memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
312                 rec->remainder);
313          block->bufp += rec->remainder;
314          block->binbuf += rec->remainder;
315       } else {
316          memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
317                 remlen);
318 #ifdef xxxxxSMCHECK
319          if (!sm_check_rtn(__FILE__, __LINE__, False)) {
320             /* We damaged a buffer */
321             Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n"
322 "rem=%d remainder=%d\n",
323                FI_to_ascii(rec->FileIndex), rec->VolSessionId,
324                stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len,
325                remlen, rec->remainder);
326             Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
327                block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
328                remlen);
329             Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
330                block->buf, block->bufp-block->buf);
331
332                Emsg0(M_ABORT, 0, _("Damaged buffer\n"));
333          }
334 #endif
335
336          block->bufp += remlen;
337          block->binbuf += remlen;
338          rec->remainder -= remlen;
339          return false;                /* did partial transfer */
340       }
341    }
342    rec->remainder = 0;                /* did whole transfer */
343    return true;
344 }
345
346
347 /*
348  * Test if we can write whole record to the block
349  *
350  *  Returns: false on failure
351  *           true  on success (all bytes can be written)
352  */
353 bool can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
354 {
355    uint32_t remlen;
356
357    remlen = block->buf_len - block->binbuf;
358    if (rec->remainder == 0) {
359       if (remlen >= WRITE_RECHDR_LENGTH) {
360          remlen -= WRITE_RECHDR_LENGTH;
361          rec->remainder = rec->data_len;
362       } else {
363          return false;
364       }
365    } else {
366       return false;
367    }
368    if (rec->remainder > 0 && remlen < rec->remainder) {
369       return false;
370    }
371    return true;
372 }
373
374
375 /*
376  * Read a Record from the block
377  *  Returns: false if nothing read or if the continuation record does not match.
378  *                 In both of these cases, a block read must be done.
379  *           true  if at least the record header was read, this
380  *                 routine may have to be called again with a new
381  *                 block if the entire record was not read.
382  */
383 bool read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec)
384 {
385    ser_declare;
386    uint32_t remlen;
387    uint32_t VolSessionId;
388    uint32_t VolSessionTime;
389    int32_t  FileIndex;
390    int32_t  Stream;
391    uint32_t data_bytes;
392    uint32_t rhl;
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(rec->FileIndex), rec->VolSessionId,
477          stream_to_ascii(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(rec->FileIndex), rec->VolSessionId,
526       stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len);
527    return true;                       /* transferred full record */
528 }