]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/record.c
382cb54792c1157caf314f7e64f416bfaaf30705
[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  */
40 char *FI_to_ascii(int fi)
41 {
42    static char buf[20];
43    if (fi >= 0) {
44       sprintf(buf, "%d", fi);
45       return buf;
46    }
47    switch (fi) {
48    case PRE_LABEL:
49       return "PRE_LABEL";
50    case VOL_LABEL:
51       return "VOL_LABEL";
52    case EOM_LABEL:
53       return "EOM_LABEL";
54    case SOS_LABEL:
55       return "SOS_LABEL";
56    case EOS_LABEL:
57       return "EOS_LABEL";
58    default:
59      sprintf(buf, "unknown: %d", fi);
60      return buf;
61    }
62 }
63
64
65 /* 
66  * Convert a Stream ID into a printable
67  * ASCII string.  Not reentrant.
68  */
69 char *stream_to_ascii(int stream)
70 {
71     static char buf[20];
72     switch (stream) {
73     case STREAM_UNIX_ATTRIBUTES:
74        return "UATTR";
75     case STREAM_FILE_DATA:
76        return "DATA";
77     case STREAM_MD5_SIGNATURE:
78        return "MD5";
79     case STREAM_GZIP_DATA:
80        return "GZIP";
81     default:
82        sprintf(buf, "%d", stream);
83        return buf;     
84     }
85 }
86
87 /* 
88  * Return a new record entity
89  */
90 DEV_RECORD *new_record(void)
91 {
92    DEV_RECORD *rec;
93
94    rec = (DEV_RECORD *) get_memory(sizeof(DEV_RECORD));
95    memset(rec, 0, sizeof(DEV_RECORD));
96    rec->data = (char *) get_pool_memory(PM_MESSAGE);
97    return rec;
98 }
99
100 /*
101  * Free the record entity 
102  *
103  */
104 void free_record(DEV_RECORD *rec) 
105 {
106    Dmsg0(150, "Enter free_record.\n");
107    free_pool_memory(rec->data);
108    Dmsg0(150, "Data buf is freed.\n");
109    free_pool_memory((POOLMEM *)rec);
110    Dmsg0(150, "Leave free_record.\n");
111
112
113 /*
114  * Read a record from a block
115  *   if necessary, read the block from the device without locking
116  *   if necessary, handle getting a new Volume
117  *
118  *  Returns: 0 on failure
119  *           1 on success
120  */
121 int read_record(DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *record)
122 {
123    Dmsg2(90, "read_record() dev=%x state=%x\n", dev, dev->state);
124
125    while (!read_record_from_block(block, record)) {
126       Dmsg2(90, "!read_record_from_block data_len=%d rem=%d\n", record->data_len,
127                 record->remainder);
128       if (!read_block_from_dev(dev, block)) {
129          /**** ****FIXME**** handle getting a new Volume */
130          Dmsg0(200, "===== Got read block I/O error ======\n");
131          return 0;
132       }
133    }
134    Dmsg4(90, "read_record FI=%s SessId=%d Strm=%s len=%d\n",
135               FI_to_ascii(record->FileIndex), record->VolSessionId, 
136               stream_to_ascii(record->Stream), record->data_len);
137    return 1;
138 }
139
140
141 /*
142  * Write a record to block 
143  *   if necessary, write the block to the device with locking
144  *   if necessary, handle getting a new Volume
145  *
146  *  Returns: 0 on failure
147  *           1 on success
148  */
149 int write_record_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *record)
150 {
151    Dmsg2(190, "write_device() dev=%x state=%x\n", dev, dev->state);
152
153    while (!write_record_to_block(block, record)) {
154       Dmsg2(190, "!write_record data_len=%d rem=%d\n", record->data_len,
155                  record->remainder);
156       if (!write_block_to_device(jcr, dev, block)) {
157          Dmsg0(90, "Got write_block_to_dev error.\n");
158          return 0;
159       }
160    }
161    Dmsg4(190, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
162       FI_to_ascii(record->FileIndex), record->VolSessionId, 
163       stream_to_ascii(record->Stream), record->data_len);
164    return 1;
165 }
166
167 /*
168  * Write a Record to the block
169  *
170  *  Returns: 0 on failure (none or partially written)
171  *           1 on success (all bytes written)
172  *
173  *  and remainder returned in packet.
174  *
175  *  We require enough room for the header, and we deal with
176  *  two special cases. 1. Only part of the record may have
177  *  been transferred the last time (when remainder is
178  *  non-zero), and 2. The remaining bytes to write may not
179  *  all fit into the block.
180  */
181 int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
182 {
183    ser_declare;
184    uint32_t remlen;
185
186    sm_check(__FILE__, __LINE__, False);
187    remlen = block->buf_len - block->binbuf;
188
189    ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
190    ASSERT(remlen >= 0);
191
192    Dmsg6(190, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n\
193 rem=%d remainder=%d\n",
194       FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
195       stream_to_ascii(rec->Stream), rec->data_len,
196       remlen, rec->remainder);
197
198    if (rec->remainder == 0) {
199       /* Require enough room to write a full header */
200       if (remlen >= RECHDR_LENGTH) {
201          ser_begin(block->bufp, RECHDR_LENGTH);
202          ser_uint32(rec->VolSessionId);
203          ser_uint32(rec->VolSessionTime);
204          ser_int32(rec->FileIndex);
205          ser_int32(rec->Stream);
206          ser_uint32(rec->data_len);
207          ASSERT(ser_length(block->bufp) == RECHDR_LENGTH);
208
209          block->bufp += RECHDR_LENGTH;
210          block->binbuf += RECHDR_LENGTH;
211          remlen -= RECHDR_LENGTH;
212          rec->remainder = rec->data_len;
213       } else {
214          rec->remainder = rec->data_len + RECHDR_LENGTH;
215          sm_check(__FILE__, __LINE__, False);
216          return 0;
217       }
218    } else {
219       /* 
220        * We are here to write unwritten bytes from a previous
221        * time. Presumably we have a new buffer (possibly 
222        * containing a volume label), so the new header 
223        * should be able to fit in the block -- otherwise we have
224        * an error.  Note, we may have to continue splitting the
225        * data record though.
226        * 
227        *  First, write the header, then write as much as 
228        * possible of the data record.
229        */
230       ser_begin(block->bufp, RECHDR_LENGTH);
231       ser_uint32(rec->VolSessionId);
232       ser_uint32(rec->VolSessionTime);
233       ser_int32(rec->FileIndex);
234       if (rec->remainder > rec->data_len) {
235          ser_int32(rec->Stream);      /* normal full header */
236          ser_uint32(rec->data_len);
237          rec->remainder = rec->data_len; /* must still do data record */
238       } else {
239          ser_int32(-rec->Stream);     /* mark this as a continuation record */
240          ser_uint32(rec->remainder);  /* bytes to do */
241       }
242       ASSERT(ser_length(block->bufp) == RECHDR_LENGTH);
243
244       /* Require enough room to write a full header */
245       ASSERT(remlen >= RECHDR_LENGTH);
246
247       block->bufp += RECHDR_LENGTH;
248       block->binbuf += RECHDR_LENGTH;
249       remlen -= RECHDR_LENGTH;
250    }
251    if (remlen == 0) {
252       sm_check(__FILE__, __LINE__, False);
253       return 0;                       /* partial transfer */
254    }
255
256    /* Now deal with data record.
257     * Part of it may have already been transferred, and we 
258     * may not have enough room to transfer the whole this time.
259     */
260    if (rec->remainder > 0) {
261       /* Write as much of data as possible */
262       if (remlen >= rec->remainder) {
263          memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
264                 rec->remainder);
265          block->bufp += rec->remainder;
266          block->binbuf += rec->remainder;
267       } else {
268          memcpy(block->bufp, rec->data+rec->data_len-rec->remainder, 
269                 remlen);
270          if (!sm_check_rtn(__FILE__, __LINE__, False)) {
271             /* We damaged a buffer */
272             Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n\
273 rem=%d remainder=%d\n",
274                FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
275                stream_to_ascii(rec->Stream), rec->data_len,
276                remlen, rec->remainder);
277             Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
278                block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
279                remlen);
280             Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
281                block->buf, block->bufp-block->buf);
282
283                Emsg0(M_ABORT, 0, "Damaged buffer\n");
284          }
285
286          block->bufp += remlen;
287          block->binbuf += remlen;
288          rec->remainder -= remlen;
289          return 0;                    /* did partial transfer */
290       }
291    }
292    rec->remainder = 0;                /* did whole transfer */
293    sm_check(__FILE__, __LINE__, False);
294    return 1;
295 }
296
297
298 /*
299  * Read a Record from the block
300  *  Returns: 0 on failure
301  *           1 on success
302  */
303 int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec)
304 {
305    ser_declare;
306    uint32_t data_bytes;
307    int32_t Stream;
308    uint32_t remlen;
309
310    remlen = block->binbuf;
311
312    /* 
313     * Get the header. There is always a full header,
314     * otherwise we find it in the next block.
315     */
316    if (remlen >= RECHDR_LENGTH) {
317       Dmsg3(90, "read_record_block: remlen=%d data_len=%d rem=%d\n", 
318             remlen, rec->data_len, rec->remainder);
319 /*    memcpy(rec->ser_buf, block->bufp, RECHDR_LENGTH); */
320
321       unser_begin(block->bufp, RECHDR_LENGTH);
322       unser_uint32(rec->VolSessionId);
323       unser_uint32(rec->VolSessionTime);
324       unser_int32(rec->FileIndex);
325       unser_int32(Stream);
326       unser_uint32(data_bytes);
327
328       ASSERT(unser_length(block->bufp) == RECHDR_LENGTH);
329       block->bufp += RECHDR_LENGTH;
330       block->binbuf -= RECHDR_LENGTH;
331       remlen -= RECHDR_LENGTH;
332
333       if (Stream < 0) {               /* continuation record? */
334          Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n", 
335             rec->remainder);
336          /* ****FIXME**** add code to verify that this
337           *  is the correct continuation of the previous
338           *  record.
339           */
340          if (!rec->remainder) {       /* if we didn't read previously */
341             rec->data_len = 0;        /* simply return data */
342          }
343          rec->Stream = -Stream;       /* set correct Stream */
344       } else {                        /* Regular record */
345          rec->Stream = Stream;
346          rec->data_len = 0;           /* transfer to beginning of data */
347       }
348       Dmsg6(90, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%d\n\
349 remlen=%d data_len=%d\n",
350          FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
351          stream_to_ascii(rec->Stream), data_bytes, remlen, rec->data_len);
352    } else {
353       Dmsg0(90, "read_record_block: nothing\n");
354       if (!rec->remainder) {
355          rec->remainder = 1;          /* set to expect continuation */
356          rec->data_len = 0;           /* no data transferred */
357       }
358       return 0;
359    }
360
361    ASSERT(data_bytes < MAX_BLOCK_LENGTH);       /* temp sanity check */
362
363    rec->data = (char *) check_pool_memory_size(rec->data, rec->data_len+data_bytes);
364    
365    /*
366     * At this point, we have read the header, now we
367     * must transfer as much of the data record as 
368     * possible taking into account: 1. A partial
369     * data record may have previously been transferred,
370     * 2. The current block may not contain the whole data
371     * record.
372     */
373    if (remlen >= data_bytes) {
374       /* Got whole record */
375       memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
376       block->bufp += data_bytes;
377       block->binbuf -= data_bytes;
378       rec->data_len += data_bytes;
379    } else {
380       /* Partial record */
381       memcpy(rec->data+rec->data_len, block->bufp, remlen);
382       block->bufp += remlen;
383       block->binbuf -= remlen;
384       rec->data_len += remlen;
385       rec->remainder = 1;             /* partial record transferred */
386       Dmsg1(90, "read_record_block: partial xfered=%d\n", rec->data_len);
387       return 0;
388    }
389    rec->remainder = 0;
390    Dmsg4(90, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
391       FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
392       stream_to_ascii(rec->Stream), rec->data_len);
393    return 1;                          /* transferred full record */
394 }