]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/record.c
Vacation updates -- see kes06Oct02
[bacula/bacula] / bacula / src / stored / record.c
1 /*
2  *
3  *   record.c -- tape record handling functions
4  *
5  *              Kern Sibbald, April MMI
6  *
7  *   Version $Id$
8  *
9  */
10 /*
11    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License as
15    published by the Free Software Foundation; either version 2 of
16    the License, or (at your option) any later version.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21    General Public License for more details.
22
23    You should have received a copy of the GNU General Public
24    License along with this program; if not, write to the Free
25    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
26    MA 02111-1307, USA.
27
28  */
29
30
31 #include "bacula.h"
32 #include "stored.h"
33
34 extern int debug_level;
35
36 /*
37  * Convert a FileIndex into a printable
38  *   ASCII string.  Not reentrant.
39  * If the FileIndex is negative, it flags the
40  *   record as a Label, otherwise it is simply
41  *   the FileIndex of the current file.
42  */
43 char *FI_to_ascii(int fi)
44 {
45    static char buf[20];
46    if (fi >= 0) {
47       sprintf(buf, "%d", fi);
48       return buf;
49    }
50    switch (fi) {
51    case PRE_LABEL:
52       return "PRE_LABEL";
53    case VOL_LABEL:
54       return "VOL_LABEL";
55    case EOM_LABEL:
56       return "EOM_LABEL";
57    case SOS_LABEL:
58       return "SOS_LABEL";
59    case EOS_LABEL:
60       return "EOS_LABEL";
61    default:
62      sprintf(buf, "unknown: %d", fi);
63      return buf;
64    }
65 }
66
67
68 /* 
69  * Convert a Stream ID into a printable
70  * ASCII string.  Not reentrant.
71  */
72 char *stream_to_ascii(int stream)
73 {
74     static char buf[20];
75     switch (stream) {
76     case STREAM_UNIX_ATTRIBUTES:
77        return "UATTR";
78     case STREAM_FILE_DATA:
79        return "DATA";
80     case STREAM_MD5_SIGNATURE:
81        return "MD5";
82     case STREAM_GZIP_DATA:
83        return "GZIP";
84     default:
85        sprintf(buf, "%d", stream);
86        return buf;     
87     }
88 }
89
90 /* 
91  * Return a new record entity
92  */
93 DEV_RECORD *new_record(void)
94 {
95    DEV_RECORD *rec;
96
97    rec = (DEV_RECORD *) get_memory(sizeof(DEV_RECORD));
98    memset(rec, 0, sizeof(DEV_RECORD));
99    rec->data = get_pool_memory(PM_MESSAGE);
100    return rec;
101 }
102
103 /*
104  * Free the record entity 
105  *
106  */
107 void free_record(DEV_RECORD *rec) 
108 {
109    Dmsg0(150, "Enter free_record.\n");
110    if (rec->data) {
111       free_pool_memory(rec->data);
112    }
113    Dmsg0(150, "Data buf is freed.\n");
114    free_pool_memory((POOLMEM *)rec);
115    Dmsg0(150, "Leave free_record.\n");
116
117
118
119 /*
120  * Write a Record to the block
121  *
122  *  Returns: 0 on failure (none or partially written)
123  *           1 on success (all bytes written)
124  *
125  *  and remainder returned in packet.
126  *
127  *  We require enough room for the header, and we deal with
128  *  two special cases. 1. Only part of the record may have
129  *  been transferred the last time (when remainder is
130  *  non-zero), and 2. The remaining bytes to write may not
131  *  all fit into the block.
132  */
133 int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
134 {
135    ser_declare;
136    uint32_t remlen;
137
138    sm_check(__FILE__, __LINE__, False);
139    remlen = block->buf_len - block->binbuf;
140
141    ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
142    ASSERT(remlen >= 0);
143
144    Dmsg6(190, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n\
145 rem=%d remainder=%d\n",
146       FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
147       stream_to_ascii(rec->Stream), rec->data_len,
148       remlen, rec->remainder);
149
150    /*
151     * If rec->remainder is non-zero, we have been called a
152     *  second (or subsequent) time to finish writing a record
153     *  that did not previously fit into the block.
154     */
155    if (rec->remainder == 0) {
156       /* Require enough room to write a full header */
157       if (remlen >= WRITE_RECHDR_LENGTH) {
158          ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
159          if (BLOCK_VER == 1) {
160          ser_uint32(rec->VolSessionId);
161          ser_uint32(rec->VolSessionTime);
162          } else {
163             block->VolSessionId = rec->VolSessionId;
164             block->VolSessionTime = rec->VolSessionTime;
165          }
166          ser_int32(rec->FileIndex);
167          ser_int32(rec->Stream);
168          ser_uint32(rec->data_len);
169
170          block->bufp += WRITE_RECHDR_LENGTH;
171          block->binbuf += WRITE_RECHDR_LENGTH;
172          remlen -= WRITE_RECHDR_LENGTH;
173          rec->remainder = rec->data_len;
174       } else {
175          rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
176          sm_check(__FILE__, __LINE__, False);
177          return 0;
178       }
179    } else {
180       /* 
181        * We are here to write unwritten bytes from a previous
182        * time. Presumably we have a new buffer (possibly 
183        * containing a volume label), so the new header 
184        * should be able to fit in the block -- otherwise we have
185        * an error.  Note, we may have to continue splitting the
186        * data record though.
187        * 
188        * First, write the header, then write as much as 
189        * possible of the data record.
190        *
191        * Every time we write a header and it is a continuation
192        * of a previous partially written record, we store the
193        * Stream as -Stream in the record header.
194        */
195       ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
196       if (BLOCK_VER == 1) {
197       ser_uint32(rec->VolSessionId);
198       ser_uint32(rec->VolSessionTime);
199       } else {
200          block->VolSessionId = rec->VolSessionId;
201          block->VolSessionTime = rec->VolSessionTime;
202       }
203       ser_int32(rec->FileIndex);
204       if (rec->remainder > rec->data_len) {
205          ser_int32(rec->Stream);      /* normal full header */
206          ser_uint32(rec->data_len);
207          rec->remainder = rec->data_len; /* must still do data record */
208       } else {
209          ser_int32(-rec->Stream);     /* mark this as a continuation record */
210          ser_uint32(rec->remainder);  /* bytes to do */
211       }
212
213       /* Require enough room to write a full header */
214       ASSERT(remlen >= WRITE_RECHDR_LENGTH);
215
216       block->bufp += WRITE_RECHDR_LENGTH;
217       block->binbuf += WRITE_RECHDR_LENGTH;
218       remlen -= WRITE_RECHDR_LENGTH;
219    }
220    if (remlen == 0) {
221       sm_check(__FILE__, __LINE__, False);
222       return 0;                       /* partial transfer */
223    }
224
225    /*
226     * Now deal with data record.
227     * Part of it may have already been transferred, and we 
228     * may not have enough room to transfer the whole this time.
229     */
230    if (rec->remainder > 0) {
231       /* Write as much of data as possible */
232       if (remlen >= rec->remainder) {
233          memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
234                 rec->remainder);
235          block->bufp += rec->remainder;
236          block->binbuf += rec->remainder;
237       } else {
238          memcpy(block->bufp, rec->data+rec->data_len-rec->remainder, 
239                 remlen);
240          if (!sm_check_rtn(__FILE__, __LINE__, False)) {
241             /* We damaged a buffer */
242             Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n\
243 rem=%d remainder=%d\n",
244                FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
245                stream_to_ascii(rec->Stream), rec->data_len,
246                remlen, rec->remainder);
247             Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
248                block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
249                remlen);
250             Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
251                block->buf, block->bufp-block->buf);
252
253                Emsg0(M_ABORT, 0, "Damaged buffer\n");
254          }
255
256          block->bufp += remlen;
257          block->binbuf += remlen;
258          rec->remainder -= remlen;
259          return 0;                    /* did partial transfer */
260       }
261    }
262    rec->remainder = 0;                /* did whole transfer */
263    sm_check(__FILE__, __LINE__, False);
264    return 1;
265 }
266
267
268 /*
269  * Test if we can write whole record to the block
270  *
271  *  Returns: 0 on failure 
272  *           1 on success (all bytes can be written)
273  */
274 int can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
275 {
276    uint32_t remlen;
277
278    remlen = block->buf_len - block->binbuf;
279    if (rec->remainder == 0) {
280       if (remlen >= WRITE_RECHDR_LENGTH) {
281          remlen -= WRITE_RECHDR_LENGTH;
282          rec->remainder = rec->data_len;
283       } else {
284          return 0;
285       }
286    } else {
287       return 0;
288    }
289    if (rec->remainder > 0 && remlen < rec->remainder) {
290       return 0;
291    }
292    return 1;
293 }
294
295
296 /*
297  * Read a Record from the block
298  *  Returns: 0 if nothing read or if the continuation record does not match.
299  *             In both of these cases, a block read must be done.
300  *           1 if at least the record header was read, this 
301  *             routine may have to be called again with a new
302  *             block if the entire record was not read.
303  */
304 int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec)
305 {
306    ser_declare;
307    uint32_t remlen;
308    uint32_t VolSessionId;
309    uint32_t VolSessionTime;
310    int32_t  FileIndex;
311    int32_t  Stream;
312    uint32_t data_bytes;
313    uint32_t rhl;
314
315    remlen = block->binbuf;
316    rec->Block = block->BlockNumber;
317
318    /* Clear state flags */
319    rec->state = 0;
320
321    /* 
322     * Get the header. There is always a full header,
323     * otherwise we find it in the next block.
324     */
325    Dmsg3(100, "Block=%d Ver=%d size=%u\n", block->BlockNumber, block->BlockVer,
326          block->block_len);
327    if (block->BlockVer == 1) {
328       rhl = RECHDR1_LENGTH;
329    } else {
330       rhl = RECHDR2_LENGTH;
331    }
332    if (remlen >= rhl) {
333       Dmsg4(90, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n", 
334             remlen, rec->data_len, rec->remainder, block->BlockVer);
335
336       unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
337       if (block->BlockVer == 1) {
338       unser_uint32(VolSessionId);
339       unser_uint32(VolSessionTime);
340       } else {
341          VolSessionId = block->VolSessionId;
342          VolSessionTime = block->VolSessionTime;
343       }
344       unser_int32(FileIndex);
345       unser_int32(Stream);
346       unser_uint32(data_bytes);
347
348       block->bufp += rhl;
349       block->binbuf -= rhl;
350       remlen -= rhl;
351
352       /* If we are looking for more (remainder!=0), we reject anything
353        *  where the VolSessionId and VolSessionTime don't agree
354        */
355       if (rec->remainder && (rec->VolSessionId != VolSessionId || 
356                              rec->VolSessionTime != VolSessionTime)) {
357          rec->state |= REC_NO_MATCH;
358          return 0;                 /* This is from some other Session */
359       }
360
361       /* if Stream is negative, it means that this is a continuation
362        * of a previous partially written record.
363        */
364       if (Stream < 0) {               /* continuation record? */
365          Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n", 
366             rec->remainder);
367          rec->state |= REC_CONTINUATION;
368          if (!rec->remainder) {       /* if we didn't read previously */
369             rec->data_len = 0;        /* return data as if no continuation */
370          } else if (rec->Stream != -Stream) {
371             rec->state |= REC_NO_MATCH;
372             return 0;                 /* This is from some other Session */
373          }
374          rec->Stream = -Stream;       /* set correct Stream */
375       } else {                        /* Regular record */
376          rec->Stream = Stream;
377          rec->data_len = 0;           /* transfer to beginning of data */
378       }
379       rec->VolSessionId = VolSessionId;
380       rec->VolSessionTime = VolSessionTime;
381       rec->FileIndex = FileIndex;
382
383       Dmsg6(100, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n\
384 remlen=%d data_len=%d\n",
385          FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
386          stream_to_ascii(rec->Stream), data_bytes, remlen, rec->data_len);
387    } else {
388       /*    
389        * No more records in this block because the number   
390        * of remaining bytes are less than a record header 
391        * length, so return empty handed, but indicate that
392        * he must read again. By returning, we allow the
393        * higher level routine to fetch the next block and
394        * then reread.
395        */
396       Dmsg0(90, "read_record_block: nothing\n");
397 #ifdef xxx
398       if (!rec->remainder) {
399          rec->remainder = 1;          /* set to expect continuation */
400          rec->data_len = 0;           /* no data transferred */
401       }
402 #endif
403       rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
404       return 0;
405    }
406
407    ASSERT(data_bytes < MAX_BLOCK_LENGTH);       /* temp sanity check */
408
409    rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
410    
411    /*
412     * At this point, we have read the header, now we
413     * must transfer as much of the data record as 
414     * possible taking into account: 1. A partial
415     * data record may have previously been transferred,
416     * 2. The current block may not contain the whole data
417     * record.
418     */
419    if (remlen >= data_bytes) {
420       /* Got whole record */
421       memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
422       block->bufp += data_bytes;
423       block->binbuf -= data_bytes;
424       rec->data_len += data_bytes;
425    } else {
426       /* Partial record */
427       memcpy(rec->data+rec->data_len, block->bufp, remlen);
428       block->bufp += remlen;
429       block->binbuf -= remlen;
430       rec->data_len += remlen;
431       rec->remainder = 1;             /* partial record transferred */
432       Dmsg1(90, "read_record_block: partial xfered=%d\n", rec->data_len);
433       rec->state |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
434       return 1;
435    }
436    rec->remainder = 0;
437    Dmsg4(90, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
438       FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
439       stream_to_ascii(rec->Stream), rec->data_len);
440    return 1;                          /* transferred full record */
441 }