]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/record.c
d7dc3eb4537efe18e6514172bc17c5df67060412
[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    free_pool_memory(rec->data);
111    Dmsg0(150, "Data buf is freed.\n");
112    free_pool_memory((POOLMEM *)rec);
113    Dmsg0(150, "Leave free_record.\n");
114
115
116 /*
117  * Read a record from a block
118  *   if necessary, read the block from the device without locking
119  *   if necessary, handle getting a new Volume
120  *
121  *  Returns: 0 on failure
122  *           1 on success
123  */
124 int read_record(DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *record)
125 {
126    Dmsg2(90, "read_record() dev=%x state=%x\n", dev, dev->state);
127
128    while (!read_record_from_block(block, record)) {
129       Dmsg2(90, "!read_record_from_block data_len=%d rem=%d\n", record->data_len,
130                 record->remainder);
131       if (!read_block_from_dev(dev, block)) {
132          Dmsg0(200, "===== Got read block I/O error ======\n");
133          return 0;
134       }
135    }
136    Dmsg4(90, "read_record FI=%s SessId=%d Strm=%s len=%d\n",
137               FI_to_ascii(record->FileIndex), record->VolSessionId, 
138               stream_to_ascii(record->Stream), record->data_len);
139    record->File = dev->file;
140    record->Block = dev->block_num;
141    return 1;
142 }
143
144
145 /*
146  * Write a record to block 
147  *   if necessary, write the block to the device with locking
148  *   if necessary, handle getting a new Volume
149  *
150  *  Returns: 0 on failure
151  *           1 on success
152  */
153 int write_record_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *record)
154 {
155    Dmsg2(190, "write_device() dev=%x state=%x\n", dev, dev->state);
156
157    while (!write_record_to_block(block, record)) {
158       Dmsg2(190, "!write_record data_len=%d rem=%d\n", record->data_len,
159                  record->remainder);
160       if (!write_block_to_device(jcr, dev, block)) {
161          Dmsg0(90, "Got write_block_to_dev error.\n");
162          return 0;
163       }
164    }
165    Dmsg4(190, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
166       FI_to_ascii(record->FileIndex), record->VolSessionId, 
167       stream_to_ascii(record->Stream), record->data_len);
168    return 1;
169 }
170
171 /*
172  * Write a Record to the block
173  *
174  *  Returns: 0 on failure (none or partially written)
175  *           1 on success (all bytes written)
176  *
177  *  and remainder returned in packet.
178  *
179  *  We require enough room for the header, and we deal with
180  *  two special cases. 1. Only part of the record may have
181  *  been transferred the last time (when remainder is
182  *  non-zero), and 2. The remaining bytes to write may not
183  *  all fit into the block.
184  */
185 int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
186 {
187    ser_declare;
188    uint32_t remlen;
189
190    sm_check(__FILE__, __LINE__, False);
191    remlen = block->buf_len - block->binbuf;
192
193    ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
194    ASSERT(remlen >= 0);
195
196    Dmsg6(190, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n\
197 rem=%d remainder=%d\n",
198       FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
199       stream_to_ascii(rec->Stream), rec->data_len,
200       remlen, rec->remainder);
201
202    /*
203     * If rec->remainder is non-zero, we have been called a
204     *  second (or subsequent) time to finish writing a record
205     *  that did not previously fit into the block.
206     */
207    if (rec->remainder == 0) {
208       /* Require enough room to write a full header */
209       if (remlen >= RECHDR_LENGTH) {
210          ser_begin(block->bufp, RECHDR_LENGTH);
211          ser_uint32(rec->VolSessionId);
212          ser_uint32(rec->VolSessionTime);
213          ser_int32(rec->FileIndex);
214          ser_int32(rec->Stream);
215          ser_uint32(rec->data_len);
216          ASSERT(ser_length(block->bufp) == RECHDR_LENGTH);
217
218          block->bufp += RECHDR_LENGTH;
219          block->binbuf += RECHDR_LENGTH;
220          remlen -= RECHDR_LENGTH;
221          rec->remainder = rec->data_len;
222       } else {
223          rec->remainder = rec->data_len + RECHDR_LENGTH;
224          sm_check(__FILE__, __LINE__, False);
225          return 0;
226       }
227    } else {
228       /* 
229        * We are here to write unwritten bytes from a previous
230        * time. Presumably we have a new buffer (possibly 
231        * containing a volume label), so the new header 
232        * should be able to fit in the block -- otherwise we have
233        * an error.  Note, we may have to continue splitting the
234        * data record though.
235        * 
236        * First, write the header, then write as much as 
237        * possible of the data record.
238        *
239        * Every time we write a header and it is a continuation
240        * of a previous partially written record, we store the
241        * Stream as -Stream in the record header.
242        */
243       ser_begin(block->bufp, RECHDR_LENGTH);
244       ser_uint32(rec->VolSessionId);
245       ser_uint32(rec->VolSessionTime);
246       ser_int32(rec->FileIndex);
247       if (rec->remainder > rec->data_len) {
248          ser_int32(rec->Stream);      /* normal full header */
249          ser_uint32(rec->data_len);
250          rec->remainder = rec->data_len; /* must still do data record */
251       } else {
252          ser_int32(-rec->Stream);     /* mark this as a continuation record */
253          ser_uint32(rec->remainder);  /* bytes to do */
254       }
255       ASSERT(ser_length(block->bufp) == RECHDR_LENGTH);
256
257       /* Require enough room to write a full header */
258       ASSERT(remlen >= RECHDR_LENGTH);
259
260       block->bufp += RECHDR_LENGTH;
261       block->binbuf += RECHDR_LENGTH;
262       remlen -= RECHDR_LENGTH;
263    }
264    if (remlen == 0) {
265       sm_check(__FILE__, __LINE__, False);
266       return 0;                       /* partial transfer */
267    }
268
269    /*
270     * Now deal with data record.
271     * Part of it may have already been transferred, and we 
272     * may not have enough room to transfer the whole this time.
273     */
274    if (rec->remainder > 0) {
275       /* Write as much of data as possible */
276       if (remlen >= rec->remainder) {
277          memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
278                 rec->remainder);
279          block->bufp += rec->remainder;
280          block->binbuf += rec->remainder;
281       } else {
282          memcpy(block->bufp, rec->data+rec->data_len-rec->remainder, 
283                 remlen);
284          if (!sm_check_rtn(__FILE__, __LINE__, False)) {
285             /* We damaged a buffer */
286             Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n\
287 rem=%d remainder=%d\n",
288                FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
289                stream_to_ascii(rec->Stream), rec->data_len,
290                remlen, rec->remainder);
291             Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
292                block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
293                remlen);
294             Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
295                block->buf, block->bufp-block->buf);
296
297                Emsg0(M_ABORT, 0, "Damaged buffer\n");
298          }
299
300          block->bufp += remlen;
301          block->binbuf += remlen;
302          rec->remainder -= remlen;
303          return 0;                    /* did partial transfer */
304       }
305    }
306    rec->remainder = 0;                /* did whole transfer */
307    sm_check(__FILE__, __LINE__, False);
308    return 1;
309 }
310
311
312 /*
313  * Read a Record from the block
314  *  Returns: 0 on failure
315  *           1 on success
316  */
317 int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec)
318 {
319    ser_declare;
320    uint32_t remlen;
321    uint32_t VolSessionId;
322    uint32_t VolSessionTime;
323    int32_t  FileIndex;
324    int32_t  Stream;
325    uint32_t data_bytes;
326
327    remlen = block->binbuf;
328
329    /* 
330     * Get the header. There is always a full header,
331     * otherwise we find it in the next block.
332     */
333    if (remlen >= RECHDR_LENGTH) {
334       Dmsg3(90, "read_record_block: remlen=%d data_len=%d rem=%d\n", 
335             remlen, rec->data_len, rec->remainder);
336
337       unser_begin(block->bufp, RECHDR_LENGTH);
338       unser_uint32(VolSessionId);
339       unser_uint32(VolSessionTime);
340       unser_int32(FileIndex);
341       unser_int32(Stream);
342       unser_uint32(data_bytes);
343
344       ASSERT(unser_length(block->bufp) == RECHDR_LENGTH);
345       block->bufp += RECHDR_LENGTH;
346       block->binbuf -= RECHDR_LENGTH;
347       remlen -= RECHDR_LENGTH;
348
349       /*    
350        * if Stream is negative, it means that this is a continuation
351        * of a previous partially written record.
352        */
353       if (Stream < 0) {               /* continuation record? */
354          Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n", 
355             rec->remainder);
356          if (!rec->remainder) {       /* if we didn't read previously */
357             rec->data_len = 0;        /* return data as if no continuation */
358          } else if (rec->VolSessionId != VolSessionId || 
359                     rec->VolSessionTime != VolSessionTime ||
360                     rec->Stream != -Stream) {
361             return 0;                 /* This is from some other Session */
362          }
363          rec->Stream = -Stream;       /* set correct Stream */
364       } else {                        /* Regular record */
365          rec->Stream = Stream;
366          rec->data_len = 0;           /* transfer to beginning of data */
367       }
368       rec->VolSessionId = VolSessionId;
369       rec->VolSessionTime = VolSessionTime;
370       rec->FileIndex = FileIndex;
371
372       Dmsg6(90, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%d\n\
373 remlen=%d data_len=%d\n",
374          FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
375          stream_to_ascii(rec->Stream), data_bytes, remlen, rec->data_len);
376    } else {
377       /*    
378        * No more records in this block because the number   
379        * of remaining bytes are less than a record header 
380        * length, so return empty handed, but indicate that
381        * he must read again. By returning, we allow the
382        * higher level routine to fetch the next block and
383        * then reread.
384        */
385       Dmsg0(90, "read_record_block: nothing\n");
386       if (!rec->remainder) {
387          rec->remainder = 1;          /* set to expect continuation */
388          rec->data_len = 0;           /* no data transferred */
389       }
390       return 0;
391    }
392
393    ASSERT(data_bytes < MAX_BLOCK_LENGTH);       /* temp sanity check */
394
395    rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
396    
397    /*
398     * At this point, we have read the header, now we
399     * must transfer as much of the data record as 
400     * possible taking into account: 1. A partial
401     * data record may have previously been transferred,
402     * 2. The current block may not contain the whole data
403     * record.
404     */
405    if (remlen >= data_bytes) {
406       /* Got whole record */
407       memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
408       block->bufp += data_bytes;
409       block->binbuf -= data_bytes;
410       rec->data_len += data_bytes;
411    } else {
412       /* Partial record */
413       memcpy(rec->data+rec->data_len, block->bufp, remlen);
414       block->bufp += remlen;
415       block->binbuf -= remlen;
416       rec->data_len += remlen;
417       rec->remainder = 1;             /* partial record transferred */
418       Dmsg1(90, "read_record_block: partial xfered=%d\n", rec->data_len);
419       return 0;
420    }
421    rec->remainder = 0;
422    Dmsg4(90, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
423       FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
424       stream_to_ascii(rec->Stream), rec->data_len);
425    return 1;                          /* transferred full record */
426 }