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