]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/record.c
kes Make sure valid argument passed to str_to_utime() where Arno
[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) 2001-2005 Kern Sibbald
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
16    version 2 as amended with additional clauses defined in the
17    file LICENSE in the main source directory.
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 
22    the file LICENSE for additional details.
23
24  */
25
26
27 #include "bacula.h"
28 #include "stored.h"
29
30 /*
31  * Convert a FileIndex into a printable
32  *   ASCII string.  Not reentrant.
33  * If the FileIndex is negative, it flags the
34  *   record as a Label, otherwise it is simply
35  *   the FileIndex of the current file.
36  */
37 const char *FI_to_ascii(char *buf, int fi)
38 {
39    if (fi >= 0) {
40       sprintf(buf, "%d", fi);
41       return buf;
42    }
43    switch (fi) {
44    case PRE_LABEL:
45       return "PRE_LABEL";
46    case VOL_LABEL:
47       return "VOL_LABEL";
48    case EOM_LABEL:
49       return "EOM_LABEL";
50    case SOS_LABEL:
51       return "SOS_LABEL";
52    case EOS_LABEL:
53       return "EOS_LABEL";
54    case EOT_LABEL:
55       return "EOT_LABEL";
56       break;
57    default:
58      sprintf(buf, _("unknown: %d"), fi);
59      return buf;
60    }
61 }
62
63
64 /*
65  * Convert a Stream ID into a printable
66  * ASCII string.  Not reentrant.
67
68  * A negative stream number represents
69  *   stream data that is continued from a
70  *   record in the previous block.
71  * If the FileIndex is negative, we are
72  *   dealing with a Label, hence the
73  *   stream is the JobId.
74  */
75 const char *stream_to_ascii(char *buf, int stream, int fi)
76 {
77     if (fi < 0) {
78        sprintf(buf, "%d", stream);
79        return buf;
80     }
81     switch (stream) {
82     case STREAM_UNIX_ATTRIBUTES:
83        return "UATTR";
84     case STREAM_FILE_DATA:
85        return "DATA";
86     case STREAM_WIN32_DATA:
87        return "WIN32-DATA";
88     case STREAM_WIN32_GZIP_DATA:
89        return "WIN32-GZIP";
90     case STREAM_MD5_DIGEST:
91        return "MD5";
92     case STREAM_SHA1_DIGEST:
93        return "SHA1";
94     case STREAM_GZIP_DATA:
95        return "GZIP";
96     case STREAM_UNIX_ATTRIBUTES_EX:
97        return "UNIX-ATTR-EX";
98     case STREAM_SPARSE_DATA:
99        return "SPARSE-DATA";
100     case STREAM_SPARSE_GZIP_DATA:
101        return "SPARSE-GZIP";
102     case STREAM_PROGRAM_NAMES:
103        return "PROG-NAMES";
104     case STREAM_PROGRAM_DATA:
105        return "PROG-DATA";
106     case STREAM_MACOS_FORK_DATA:
107        return "MACOS-RSRC";
108     case STREAM_HFSPLUS_ATTRIBUTES:
109        return "HFSPLUS-ATTR";
110     case STREAM_SHA256_DIGEST:
111        return "SHA256";
112     case STREAM_SHA512_DIGEST:
113        return "SHA512";
114     case STREAM_SIGNED_DIGEST:
115        return "SIGNED-DIGEST";
116     case STREAM_ENCRYPTED_SESSION_DATA:
117        return "ENCRYPTED-SESSION-DATA";
118     case STREAM_ENCRYPTED_FILE_DATA:
119        return "ENCRYPTED-FILE";
120     case STREAM_ENCRYPTED_FILE_GZIP_DATA:
121        return "ENCRYPTED-GZIP";
122     case STREAM_ENCRYPTED_WIN32_DATA:
123        return "ENCRYPTED-WIN32-DATA";
124     case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
125        return "ENCRYPTED-WIN32-GZIP";
126     case STREAM_ENCRYPTED_MACOS_FORK_DATA:
127        return "ENCRYPTED-MACOS-RSRC";
128     case -STREAM_UNIX_ATTRIBUTES:
129        return "contUATTR";
130     case -STREAM_FILE_DATA:
131        return "contDATA";
132     case -STREAM_WIN32_DATA:
133        return "contWIN32-DATA";
134     case -STREAM_WIN32_GZIP_DATA:
135        return "contWIN32-GZIP";
136     case -STREAM_MD5_DIGEST:
137        return "contMD5";
138     case -STREAM_SHA1_DIGEST:
139        return "contSHA1";
140     case -STREAM_GZIP_DATA:
141        return "contGZIP";
142     case -STREAM_UNIX_ATTRIBUTES_EX:
143        return "contUNIX-ATTR-EX";
144     case -STREAM_SPARSE_DATA:
145        return "contSPARSE-DATA";
146     case -STREAM_SPARSE_GZIP_DATA:
147        return "contSPARSE-GZIP";
148     case -STREAM_PROGRAM_NAMES:
149        return "contPROG-NAMES";
150     case -STREAM_PROGRAM_DATA:
151        return "contPROG-DATA";
152     case -STREAM_MACOS_FORK_DATA:
153        return "contMACOS-RSRC";
154     case -STREAM_HFSPLUS_ATTRIBUTES:
155        return "contHFSPLUS-ATTR";
156     case -STREAM_SHA256_DIGEST:
157        return "contSHA256";
158     case -STREAM_SHA512_DIGEST:
159        return "contSHA512";
160     case -STREAM_SIGNED_DIGEST:
161        return "contSIGNED-DIGEST";
162     case -STREAM_ENCRYPTED_SESSION_DATA:
163        return "contENCRYPTED-SESSION-DATA";
164     case -STREAM_ENCRYPTED_FILE_DATA:
165        return "contENCRYPTED-FILE";
166     case -STREAM_ENCRYPTED_FILE_GZIP_DATA:
167        return "contENCRYPTED-GZIP";
168     case -STREAM_ENCRYPTED_WIN32_DATA:
169        return "contENCRYPTED-WIN32-DATA";
170     case -STREAM_ENCRYPTED_WIN32_GZIP_DATA:
171        return "contENCRYPTED-WIN32-GZIP";
172     case -STREAM_ENCRYPTED_MACOS_FORK_DATA:
173        return "contENCRYPTED-MACOS-RSRC";
174     default:
175        sprintf(buf, "%d", stream);
176        return buf;
177     }
178 }
179
180 /*
181  * Return a new record entity
182  */
183 DEV_RECORD *new_record(void)
184 {
185    DEV_RECORD *rec;
186
187    rec = (DEV_RECORD *)get_memory(sizeof(DEV_RECORD));
188    memset(rec, 0, sizeof(DEV_RECORD));
189    rec->data = get_pool_memory(PM_MESSAGE);
190    return rec;
191 }
192
193 void empty_record(DEV_RECORD *rec)
194 {
195    rec->File = rec->Block = 0;
196    rec->VolSessionId = rec->VolSessionTime = 0;
197    rec->FileIndex = rec->Stream = 0;
198    rec->data_len = rec->remainder = 0;
199    rec->state &= ~(REC_PARTIAL_RECORD|REC_BLOCK_EMPTY|REC_NO_MATCH|REC_CONTINUATION);
200 }
201
202 /*
203  * Free the record entity
204  *
205  */
206 void free_record(DEV_RECORD *rec)
207 {
208    Dmsg0(950, "Enter free_record.\n");
209    if (rec->data) {
210       free_pool_memory(rec->data);
211    }
212    Dmsg0(950, "Data buf is freed.\n");
213    free_pool_memory((POOLMEM *)rec);
214    Dmsg0(950, "Leave free_record.\n");
215 }
216
217
218 /*
219  * Write a Record to the block
220  *
221  *  Returns: false on failure (none or partially written)
222  *           true  on success (all bytes written)
223  *
224  *  and remainder returned in packet.
225  *
226  *  We require enough room for the header, and we deal with
227  *  two special cases. 1. Only part of the record may have
228  *  been transferred the last time (when remainder is
229  *  non-zero), and 2. The remaining bytes to write may not
230  *  all fit into the block.
231  */
232 bool write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
233 {
234    ser_declare;
235    uint32_t remlen;
236    char buf1[100], buf2[100];
237
238    remlen = block->buf_len - block->binbuf;
239
240    ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
241    ASSERT(block->buf_len >= block->binbuf);
242
243    Dmsg6(890, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n"
244 "rem=%d remainder=%d\n",
245       FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
246       stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
247       remlen, rec->remainder);
248
249    /*
250     * If rec->remainder is non-zero, we have been called a
251     *  second (or subsequent) time to finish writing a record
252     *  that did not previously fit into the block.
253     */
254    if (rec->remainder == 0) {
255       /* Require enough room to write a full header */
256       if (remlen >= WRITE_RECHDR_LENGTH) {
257          ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
258          if (BLOCK_VER == 1) {
259             ser_uint32(rec->VolSessionId);
260             ser_uint32(rec->VolSessionTime);
261          } else {
262             block->VolSessionId = rec->VolSessionId;
263             block->VolSessionTime = rec->VolSessionTime;
264          }
265          ser_int32(rec->FileIndex);
266          ser_int32(rec->Stream);
267          ser_uint32(rec->data_len);
268
269          block->bufp += WRITE_RECHDR_LENGTH;
270          block->binbuf += WRITE_RECHDR_LENGTH;
271          remlen -= WRITE_RECHDR_LENGTH;
272          rec->remainder = rec->data_len;
273          if (rec->FileIndex > 0) {
274             /* If data record, update what we have in this block */
275             if (block->FirstIndex == 0) {
276                block->FirstIndex = rec->FileIndex;
277             }
278             block->LastIndex = rec->FileIndex;
279          }
280       } else {
281          rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
282          return false;
283       }
284    } else {
285       /*
286        * We are here to write unwritten bytes from a previous
287        * time. Presumably we have a new buffer (possibly
288        * containing a volume label), so the new header
289        * should be able to fit in the block -- otherwise we have
290        * an error.  Note, we have to continue splitting the
291        * data record if it is longer than the block.
292        *
293        * First, write the header, then write as much as
294        * possible of the data record.
295        *
296        * Every time we write a header and it is a continuation
297        * of a previous partially written record, we store the
298        * Stream as -Stream in the record header.
299        */
300       ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
301       if (BLOCK_VER == 1) {
302          ser_uint32(rec->VolSessionId);
303          ser_uint32(rec->VolSessionTime);
304       } else {
305          block->VolSessionId = rec->VolSessionId;
306          block->VolSessionTime = rec->VolSessionTime;
307       }
308       ser_int32(rec->FileIndex);
309       if (rec->remainder > rec->data_len) {
310          ser_int32(rec->Stream);      /* normal full header */
311          ser_uint32(rec->data_len);
312          rec->remainder = rec->data_len; /* must still do data record */
313       } else {
314          ser_int32(-rec->Stream);     /* mark this as a continuation record */
315          ser_uint32(rec->remainder);  /* bytes to do */
316       }
317
318       /* Require enough room to write a full header */
319       ASSERT(remlen >= WRITE_RECHDR_LENGTH);
320
321       block->bufp += WRITE_RECHDR_LENGTH;
322       block->binbuf += WRITE_RECHDR_LENGTH;
323       remlen -= WRITE_RECHDR_LENGTH;
324       if (rec->FileIndex > 0) {
325          /* If data record, update what we have in this block */
326          if (block->FirstIndex == 0) {
327             block->FirstIndex = rec->FileIndex;
328          }
329          block->LastIndex = rec->FileIndex;
330       }
331    }
332    if (remlen == 0) {
333       return false;                   /* partial transfer */
334    }
335
336    /*
337     * Now deal with data record.
338     * Part of it may have already been transferred, and we
339     * may not have enough room to transfer the whole this time.
340     */
341    if (rec->remainder > 0) {
342       /* Write as much of data as possible */
343       if (remlen >= rec->remainder) {
344          memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
345                 rec->remainder);
346          block->bufp += rec->remainder;
347          block->binbuf += rec->remainder;
348       } else {
349          memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
350                 remlen);
351 #ifdef xxxxxSMCHECK
352          if (!sm_check_rtn(__FILE__, __LINE__, False)) {
353             /* We damaged a buffer */
354             Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n"
355 "rem=%d remainder=%d\n",
356                FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
357                stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
358                remlen, rec->remainder);
359             Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
360                block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
361                remlen);
362             Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
363                block->buf, block->bufp-block->buf);
364
365                Emsg0(M_ABORT, 0, _("Damaged buffer\n"));
366          }
367 #endif
368
369          block->bufp += remlen;
370          block->binbuf += remlen;
371          rec->remainder -= remlen;
372          return false;                /* did partial transfer */
373       }
374    }
375    rec->remainder = 0;                /* did whole transfer */
376    return true;
377 }
378
379
380 /*
381  * Test if we can write whole record to the block
382  *
383  *  Returns: false on failure
384  *           true  on success (all bytes can be written)
385  */
386 bool can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
387 {
388    uint32_t remlen;
389
390    remlen = block->buf_len - block->binbuf;
391    if (rec->remainder == 0) {
392       if (remlen >= WRITE_RECHDR_LENGTH) {
393          remlen -= WRITE_RECHDR_LENGTH;
394          rec->remainder = rec->data_len;
395       } else {
396          return false;
397       }
398    } else {
399       return false;
400    }
401    if (rec->remainder > 0 && remlen < rec->remainder) {
402       return false;
403    }
404    return true;
405 }
406
407
408 /*
409  * Read a Record from the block
410  *  Returns: false if nothing read or if the continuation record does not match.
411  *                 In both of these cases, a block read must be done.
412  *           true  if at least the record header was read, this
413  *                 routine may have to be called again with a new
414  *                 block if the entire record was not read.
415  */
416 bool read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec)
417 {
418    ser_declare;
419    uint32_t remlen;
420    uint32_t VolSessionId;
421    uint32_t VolSessionTime;
422    int32_t  FileIndex;
423    int32_t  Stream;
424    uint32_t data_bytes;
425    uint32_t rhl;
426    char buf1[100], buf2[100];
427
428    remlen = block->binbuf;
429    rec->Block = block->BlockNumber;
430    rec->File = ((DEVICE *)block->dev)->file;
431
432    /* Clear state flags */
433    rec->state = 0;
434    if (block->dev->is_tape()) {
435       rec->state |= REC_ISTAPE;
436    }
437
438
439    /*
440     * Get the header. There is always a full header,
441     * otherwise we find it in the next block.
442     */
443    Dmsg3(450, "Block=%d Ver=%d size=%u\n", block->BlockNumber, block->BlockVer,
444          block->block_len);
445    if (block->BlockVer == 1) {
446       rhl = RECHDR1_LENGTH;
447    } else {
448       rhl = RECHDR2_LENGTH;
449    }
450    if (remlen >= rhl) {
451       Dmsg4(450, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n",
452             remlen, rec->data_len, rec->remainder, block->BlockVer);
453
454       unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
455       if (block->BlockVer == 1) {
456          unser_uint32(VolSessionId);
457          unser_uint32(VolSessionTime);
458       } else {
459          VolSessionId = block->VolSessionId;
460          VolSessionTime = block->VolSessionTime;
461       }
462       unser_int32(FileIndex);
463       unser_int32(Stream);
464       unser_uint32(data_bytes);
465
466       block->bufp += rhl;
467       block->binbuf -= rhl;
468       remlen -= rhl;
469
470       /* If we are looking for more (remainder!=0), we reject anything
471        *  where the VolSessionId and VolSessionTime don't agree
472        */
473       if (rec->remainder && (rec->VolSessionId != VolSessionId ||
474                              rec->VolSessionTime != VolSessionTime)) {
475          rec->state |= REC_NO_MATCH;
476          Dmsg0(450, "remainder and VolSession doesn't match\n");
477          return false;             /* This is from some other Session */
478       }
479
480       /* if Stream is negative, it means that this is a continuation
481        * of a previous partially written record.
482        */
483       if (Stream < 0) {               /* continuation record? */
484          Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
485             rec->remainder);
486          rec->state |= REC_CONTINUATION;
487          if (!rec->remainder) {       /* if we didn't read previously */
488             rec->data_len = 0;        /* return data as if no continuation */
489          } else if (rec->Stream != -Stream) {
490             rec->state |= REC_NO_MATCH;
491             return false;             /* This is from some other Session */
492          }
493          rec->Stream = -Stream;       /* set correct Stream */
494       } else {                        /* Regular record */
495          rec->Stream = Stream;
496          rec->data_len = 0;           /* transfer to beginning of data */
497       }
498       rec->VolSessionId = VolSessionId;
499       rec->VolSessionTime = VolSessionTime;
500       rec->FileIndex = FileIndex;
501       if (FileIndex > 0) {
502          if (block->FirstIndex == 0) {
503             block->FirstIndex = FileIndex;
504          }
505          block->LastIndex = FileIndex;
506       }
507
508       Dmsg6(450, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
509                  "remlen=%d data_len=%d\n",
510          FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
511          stream_to_ascii(buf2, rec->Stream, rec->FileIndex), data_bytes, remlen,
512          rec->data_len);
513    } else {
514       /*
515        * No more records in this block because the number
516        * of remaining bytes are less than a record header
517        * length, so return empty handed, but indicate that
518        * he must read again. By returning, we allow the
519        * higher level routine to fetch the next block and
520        * then reread.
521        */
522       Dmsg0(450, "read_record_block: nothing\n");
523       rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
524       empty_block(block);                      /* mark block empty */
525       return false;
526    }
527
528    ASSERT(data_bytes < MAX_BLOCK_LENGTH);       /* temp sanity check */
529
530    rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
531
532    /*
533     * At this point, we have read the header, now we
534     * must transfer as much of the data record as
535     * possible taking into account: 1. A partial
536     * data record may have previously been transferred,
537     * 2. The current block may not contain the whole data
538     * record.
539     */
540    if (remlen >= data_bytes) {
541       /* Got whole record */
542       memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
543       block->bufp += data_bytes;
544       block->binbuf -= data_bytes;
545       rec->data_len += data_bytes;
546    } else {
547       /* Partial record */
548       memcpy(rec->data+rec->data_len, block->bufp, remlen);
549       block->bufp += remlen;
550       block->binbuf -= remlen;
551       rec->data_len += remlen;
552       rec->remainder = 1;             /* partial record transferred */
553       Dmsg1(450, "read_record_block: partial xfered=%d\n", rec->data_len);
554       rec->state |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
555       return 1;
556    }
557    rec->remainder = 0;
558    Dmsg4(450, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
559       FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
560       stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
561    return true;                       /* transferred full record */
562 }