]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/record.c
Clean up some SD message nos.
[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-2003 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 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 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_MD5_SIGNATURE:
96        return "MD5";
97     case STREAM_SHA1_SIGNATURE:
98        return "SHA1";
99     case STREAM_GZIP_DATA:
100        return "GZIP";
101     case STREAM_WIN32_ATTRIBUTES:
102        return "WIN32-ATTR";
103     case STREAM_SPARSE_DATA:
104        return "SPARSE-DATA";
105     case STREAM_SPARSE_GZIP_DATA:
106        return "SPARSE-GZIP";
107     case STREAM_PROGRAM_NAMES:
108        return "PROG-NAMES";
109     case STREAM_PROGRAM_DATA:
110        return "PROG-DATA";
111     case -STREAM_UNIX_ATTRIBUTES:
112        return "contUATTR";
113     case -STREAM_FILE_DATA:
114        return "contDATA";
115     case -STREAM_MD5_SIGNATURE:
116        return "contMD5";
117     case -STREAM_SHA1_SIGNATURE:
118        return "contSHA1";
119     case -STREAM_GZIP_DATA:
120        return "contGZIP";
121     case -STREAM_WIN32_ATTRIBUTES:
122        return "contWIN32-ATTR";
123     case -STREAM_SPARSE_DATA:
124        return "contSPARSE-DATA";
125     case -STREAM_SPARSE_GZIP_DATA:
126        return "contSPARSE-GZIP";
127     case -STREAM_PROGRAM_NAMES:
128        return "contPROG-NAMES";
129     case -STREAM_PROGRAM_DATA:
130        return "contPROG-DATA";
131     default:
132        sprintf(buf, "%d", stream);
133        return buf;     
134     }
135 }
136
137 /* 
138  * Return a new record entity
139  */
140 DEV_RECORD *new_record(void)
141 {
142    DEV_RECORD *rec;
143
144    rec = (DEV_RECORD *) get_memory(sizeof(DEV_RECORD));
145    memset(rec, 0, sizeof(DEV_RECORD));
146    rec->data = get_pool_memory(PM_MESSAGE);
147    return rec;
148 }
149
150 /*
151  * Free the record entity 
152  *
153  */
154 void free_record(DEV_RECORD *rec) 
155 {
156    Dmsg0(150, "Enter free_record.\n");
157    if (rec->data) {
158       free_pool_memory(rec->data);
159    }
160    Dmsg0(150, "Data buf is freed.\n");
161    free_pool_memory((POOLMEM *)rec);
162    Dmsg0(150, "Leave free_record.\n");
163
164
165
166 /*
167  * Write a Record to the block
168  *
169  *  Returns: 0 on failure (none or partially written)
170  *           1 on success (all bytes written)
171  *
172  *  and remainder returned in packet.
173  *
174  *  We require enough room for the header, and we deal with
175  *  two special cases. 1. Only part of the record may have
176  *  been transferred the last time (when remainder is
177  *  non-zero), and 2. The remaining bytes to write may not
178  *  all fit into the block.
179  */
180 int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
181 {
182    ser_declare;
183    uint32_t remlen;
184
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->FileIndex), rec->data_len,
194       remlen, rec->remainder);
195
196    /*
197     * If rec->remainder is non-zero, we have been called a
198     *  second (or subsequent) time to finish writing a record
199     *  that did not previously fit into the block.
200     */
201    if (rec->remainder == 0) {
202       /* Require enough room to write a full header */
203       if (remlen >= WRITE_RECHDR_LENGTH) {
204          ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
205          if (BLOCK_VER == 1) {
206          ser_uint32(rec->VolSessionId);
207          ser_uint32(rec->VolSessionTime);
208          } else {
209             block->VolSessionId = rec->VolSessionId;
210             block->VolSessionTime = rec->VolSessionTime;
211          }
212          ser_int32(rec->FileIndex);
213          ser_int32(rec->Stream);
214          ser_uint32(rec->data_len);
215
216          block->bufp += WRITE_RECHDR_LENGTH;
217          block->binbuf += WRITE_RECHDR_LENGTH;
218          remlen -= WRITE_RECHDR_LENGTH;
219          rec->remainder = rec->data_len;
220       } else {
221          rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
222          return 0;
223       }
224    } else {
225       /* 
226        * We are here to write unwritten bytes from a previous
227        * time. Presumably we have a new buffer (possibly 
228        * containing a volume label), so the new header 
229        * should be able to fit in the block -- otherwise we have
230        * an error.  Note, we have to continue splitting the
231        * data record if it is longer than the block.
232        * 
233        * First, write the header, then write as much as 
234        * possible of the data record.
235        *
236        * Every time we write a header and it is a continuation
237        * of a previous partially written record, we store the
238        * Stream as -Stream in the record header.
239        */
240       ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
241       if (BLOCK_VER == 1) {
242       ser_uint32(rec->VolSessionId);
243       ser_uint32(rec->VolSessionTime);
244       } else {
245          block->VolSessionId = rec->VolSessionId;
246          block->VolSessionTime = rec->VolSessionTime;
247       }
248       ser_int32(rec->FileIndex);
249       if (rec->remainder > rec->data_len) {
250          ser_int32(rec->Stream);      /* normal full header */
251          ser_uint32(rec->data_len);
252          rec->remainder = rec->data_len; /* must still do data record */
253       } else {
254          ser_int32(-rec->Stream);     /* mark this as a continuation record */
255          ser_uint32(rec->remainder);  /* bytes to do */
256       }
257
258       /* Require enough room to write a full header */
259       ASSERT(remlen >= WRITE_RECHDR_LENGTH);
260
261       block->bufp += WRITE_RECHDR_LENGTH;
262       block->binbuf += WRITE_RECHDR_LENGTH;
263       remlen -= WRITE_RECHDR_LENGTH;
264    }
265    if (remlen == 0) {
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 #ifdef xxxxxSMCHECK
285          if (!sm_check_rtn(__FILE__, __LINE__, False)) {
286             /* We damaged a buffer */
287             Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n\
288 rem=%d remainder=%d\n",
289                FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
290                stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len,
291                remlen, rec->remainder);
292             Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
293                block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
294                remlen);
295             Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
296                block->buf, block->bufp-block->buf);
297
298                Emsg0(M_ABORT, 0, "Damaged buffer\n");
299          }
300 #endif
301
302          block->bufp += remlen;
303          block->binbuf += remlen;
304          rec->remainder -= remlen;
305          return 0;                    /* did partial transfer */
306       }
307    }
308    rec->remainder = 0;                /* did whole transfer */
309    return 1;
310 }
311
312
313 /*
314  * Test if we can write whole record to the block
315  *
316  *  Returns: 0 on failure 
317  *           1 on success (all bytes can be written)
318  */
319 int can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
320 {
321    uint32_t remlen;
322
323    remlen = block->buf_len - block->binbuf;
324    if (rec->remainder == 0) {
325       if (remlen >= WRITE_RECHDR_LENGTH) {
326          remlen -= WRITE_RECHDR_LENGTH;
327          rec->remainder = rec->data_len;
328       } else {
329          return 0;
330       }
331    } else {
332       return 0;
333    }
334    if (rec->remainder > 0 && remlen < rec->remainder) {
335       return 0;
336    }
337    return 1;
338 }
339
340
341 /*
342  * Read a Record from the block
343  *  Returns: 0 if nothing read or if the continuation record does not match.
344  *             In both of these cases, a block read must be done.
345  *           1 if at least the record header was read, this 
346  *             routine may have to be called again with a new
347  *             block if the entire record was not read.
348  */
349 int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec)
350 {
351    ser_declare;
352    uint32_t remlen;
353    uint32_t VolSessionId;
354    uint32_t VolSessionTime;
355    int32_t  FileIndex;
356    int32_t  Stream;
357    uint32_t data_bytes;
358    uint32_t rhl;
359
360    remlen = block->binbuf;
361    rec->Block = block->BlockNumber;
362    rec->File = ((DEVICE *)block->dev)->file;
363
364    /* Clear state flags */       
365    rec->state = 0;
366    if (((DEVICE *)block->dev)->state & ST_TAPE) {
367       rec->state |= REC_ISTAPE;
368    }
369
370
371    /* 
372     * Get the header. There is always a full header,
373     * otherwise we find it in the next block.
374     */
375    Dmsg3(100, "Block=%d Ver=%d size=%u\n", block->BlockNumber, block->BlockVer,
376          block->block_len);
377    if (block->BlockVer == 1) {
378       rhl = RECHDR1_LENGTH;
379    } else {
380       rhl = RECHDR2_LENGTH;
381    }
382    if (remlen >= rhl) {
383       Dmsg4(90, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n", 
384             remlen, rec->data_len, rec->remainder, block->BlockVer);
385
386       unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
387       if (block->BlockVer == 1) {
388       unser_uint32(VolSessionId);
389       unser_uint32(VolSessionTime);
390       } else {
391          VolSessionId = block->VolSessionId;
392          VolSessionTime = block->VolSessionTime;
393       }
394       unser_int32(FileIndex);
395       unser_int32(Stream);
396       unser_uint32(data_bytes);
397
398       block->bufp += rhl;
399       block->binbuf -= rhl;
400       remlen -= rhl;
401
402       /* If we are looking for more (remainder!=0), we reject anything
403        *  where the VolSessionId and VolSessionTime don't agree
404        */
405       if (rec->remainder && (rec->VolSessionId != VolSessionId || 
406                              rec->VolSessionTime != VolSessionTime)) {
407          rec->state |= REC_NO_MATCH;
408          return 0;                 /* This is from some other Session */
409       }
410
411       /* if Stream is negative, it means that this is a continuation
412        * of a previous partially written record.
413        */
414       if (Stream < 0) {               /* continuation record? */
415          Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n", 
416             rec->remainder);
417          rec->state |= REC_CONTINUATION;
418          if (!rec->remainder) {       /* if we didn't read previously */
419             rec->data_len = 0;        /* return data as if no continuation */
420          } else if (rec->Stream != -Stream) {
421             rec->state |= REC_NO_MATCH;
422             return 0;                 /* This is from some other Session */
423          }
424          rec->Stream = -Stream;       /* set correct Stream */
425       } else {                        /* Regular record */
426          rec->Stream = Stream;
427          rec->data_len = 0;           /* transfer to beginning of data */
428       }
429       rec->VolSessionId = VolSessionId;
430       rec->VolSessionTime = VolSessionTime;
431       rec->FileIndex = FileIndex;
432
433       Dmsg6(100, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n\
434 remlen=%d data_len=%d\n",
435          FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
436          stream_to_ascii(rec->Stream, rec->FileIndex), data_bytes, remlen, 
437          rec->data_len);
438    } else {
439       /*    
440        * No more records in this block because the number   
441        * of remaining bytes are less than a record header 
442        * length, so return empty handed, but indicate that
443        * he must read again. By returning, we allow the
444        * higher level routine to fetch the next block and
445        * then reread.
446        */
447       Dmsg0(90, "read_record_block: nothing\n");
448 #ifdef xxx
449       if (!rec->remainder) {
450          rec->remainder = 1;          /* set to expect continuation */
451          rec->data_len = 0;           /* no data transferred */
452       }
453 #endif
454       rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
455       empty_block(block);                      /* mark block empty */
456       return 0;
457    }
458
459    ASSERT(data_bytes < MAX_BLOCK_LENGTH);       /* temp sanity check */
460
461    rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
462    
463    /*
464     * At this point, we have read the header, now we
465     * must transfer as much of the data record as 
466     * possible taking into account: 1. A partial
467     * data record may have previously been transferred,
468     * 2. The current block may not contain the whole data
469     * record.
470     */
471    if (remlen >= data_bytes) {
472       /* Got whole record */
473       memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
474       block->bufp += data_bytes;
475       block->binbuf -= data_bytes;
476       rec->data_len += data_bytes;
477    } else {
478       /* Partial record */
479       memcpy(rec->data+rec->data_len, block->bufp, remlen);
480       block->bufp += remlen;
481       block->binbuf -= remlen;
482       rec->data_len += remlen;
483       rec->remainder = 1;             /* partial record transferred */
484       Dmsg1(90, "read_record_block: partial xfered=%d\n", rec->data_len);
485       rec->state |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
486       return 1;
487    }
488    rec->remainder = 0;
489    Dmsg4(90, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
490       FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
491       stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len);
492    return 1;                          /* transferred full record */
493 }