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