]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/record_write.c
Backport from Bacula Enterprise
[bacula/bacula] / bacula / src / stored / record_write.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5    Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6
7    The original author of Bacula is Kern Sibbald, with contributions
8    from many others, a complete list can be found in the file AUTHORS.
9
10    You may use this file and others of this release according to the
11    license defined in the LICENSE file, which includes the Affero General
12    Public License, v3.0 ("AGPLv3") and some additional permissions and
13    terms pursuant to its AGPLv3 Section 7.
14
15    This notice must be preserved when any source code is 
16    conveyed and/or propagated.
17
18    Bacula(R) is a registered trademark of Kern Sibbald.
19 */
20 /*
21  *
22  *   record_write.c -- Volume (tape/disk) record write functions
23  *
24  *            Kern Sibbald, April MMI
25  *              added BB02 format October MMII
26  *
27  */
28
29
30 #include "bacula.h"
31 #include "stored.h"
32
33 /* Imported functions */
34
35 static const int dbgep = 250;         /* debug execution path */
36 static const int dbgel = 250;         /* debug Enter/Leave code */
37
38 struct rechdr {
39    int32_t FileIndex;
40    uint32_t data_len;
41    uint32_t reclen;
42    int32_t Stream;
43    int32_t oStream;
44 };
45
46 /*
47  * Flush block to disk
48  */
49 bool flush_block(DCR *dcr)
50 {
51    bool rtn = false;
52
53    if (!is_block_empty(dcr->block)) {
54       Dmsg0(dbgep, "=== wpath 53 flush_block\n");
55       Dmsg3(190, "Call flush_block BlockAddr=%lld nbytes=%d block=%x\n",
56          dcr->block->BlockAddr, dcr->block->binbuf, dcr->block);
57       dump_block(dcr->block, "Flush_block");
58       if (dcr->jcr->is_canceled() || !dcr->write_block_to_device()) {
59          Dmsg0(dbgep, "=== wpath 54 flush_block\n");
60          Dmsg0(190, "Failed to write block to device, return false.\n");
61          goto get_out;
62       }
63       empty_block(dcr->block);
64    }
65    rtn = true;
66
67 get_out:
68    return rtn;
69 }
70
71 /*
72  * Write a header record to the block.
73  */
74 static bool write_header_to_block(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
75 {
76    ser_declare;
77
78    Dmsg0(dbgep, "=== wpath 11 write_header_to_block\n");
79    rec->remlen = block->buf_len - block->binbuf;
80    /* Require enough room to write a full header */
81    if (rec->remlen < WRITE_RECHDR_LENGTH) {
82       Dmsg0(dbgep, "=== wpath 12 write_header_to_block\n");
83       Dmsg4(190, "Fail remlen=%d<%d reclen buf_len=%d binbuf=%d\n",
84          rec->remlen, WRITE_RECHDR_LENGTH, block->buf_len, block->binbuf);
85       rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
86       return false;
87    }
88    ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
89    if (BLOCK_VER == 1) {
90       Dmsg0(dbgep, "=== wpath 13 write_header_to_block\n");
91       ser_uint32(rec->VolSessionId);
92       ser_uint32(rec->VolSessionTime);
93    } else {
94       Dmsg0(dbgep, "=== wpath 14 write_header_to_block\n");
95       block->VolSessionId = rec->VolSessionId;
96       block->VolSessionTime = rec->VolSessionTime;
97    }
98    ser_int32(rec->FileIndex);
99    ser_int32(rec->Stream);
100    ser_uint32(rec->data_len);
101
102    block->bufp += WRITE_RECHDR_LENGTH;
103    block->binbuf += WRITE_RECHDR_LENGTH;
104    rec->remlen -= WRITE_RECHDR_LENGTH;
105    rec->remainder = rec->data_len;
106    if (rec->FileIndex > 0) {
107       Dmsg0(dbgep, "=== wpath 15 write_header_to_block\n");
108       /* If data record, update what we have in this block */
109       if (block->FirstIndex == 0) {
110          Dmsg0(dbgep, "=== wpath 16 write_header_to_block\n");
111          block->FirstIndex = rec->FileIndex;
112       }
113       block->LastIndex = rec->FileIndex;
114    }
115
116    //dump_block(block, "Add header");
117    return true;
118 }
119
120 /*
121  * If the prior block was not big enough to hold the
122  *  whole record, write a continuation header record.
123  */
124 static void write_continue_header_to_block(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
125 {
126    ser_declare;
127
128    Dmsg0(dbgep, "=== wpath 17 write_cont_hdr_to_block\n");
129    rec->remlen = block->buf_len - block->binbuf;
130    /*
131     * We have unwritten bytes from a previous
132     * time. Presumably we have a new buffer (possibly
133     * containing a volume label), so the new header
134     * should be able to fit in the block -- otherwise we have
135     * an error.  Note, we have to continue splitting the
136     * data record if it is longer than the block.
137     *
138     * First, write the header.
139     *
140     * Every time we write a header and it is a continuation
141     * of a previous partially written record, we store the
142     * Stream as -Stream in the record header.
143     */
144    ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
145    if (BLOCK_VER == 1) {
146       Dmsg0(dbgep, "=== wpath 18 write_cont_hdr_to_block\n");
147       ser_uint32(rec->VolSessionId);
148       ser_uint32(rec->VolSessionTime);
149    } else {
150       Dmsg0(dbgep, "=== wpath 19 write_cont_hdr_to_block\n");
151       block->VolSessionId = rec->VolSessionId;
152       block->VolSessionTime = rec->VolSessionTime;
153    }
154    ser_int32(rec->FileIndex);
155    if (rec->remainder > rec->data_len) {
156       Dmsg0(dbgep, "=== wpath 20 write_cont_hdr_to_block\n");
157       ser_int32(rec->Stream);      /* normal full header */
158       ser_uint32(rec->data_len);
159       rec->remainder = rec->data_len; /* must still do data record */
160    } else {
161       Dmsg0(dbgep, "=== wpath 21 write_cont_hdr_to_block\n");
162       ser_int32(-rec->Stream);     /* mark this as a continuation record */
163       ser_uint32(rec->remainder);  /* bytes to do */
164    }
165
166    /* Require enough room to write a full header */
167    ASSERT(rec->remlen >= WRITE_RECHDR_LENGTH);
168
169    block->bufp += WRITE_RECHDR_LENGTH;
170    block->binbuf += WRITE_RECHDR_LENGTH;
171    rec->remlen -= WRITE_RECHDR_LENGTH;
172    if (rec->FileIndex > 0) {
173       Dmsg0(dbgep, "=== wpath 22 write_cont_hdr_to_block\n");
174       /* If data record, update what we have in this block */
175       if (block->FirstIndex == 0) {
176          Dmsg0(dbgep, "=== wpath 23 write_cont_hdr_to_block\n");
177          block->FirstIndex = rec->FileIndex;
178       }
179       block->LastIndex = rec->FileIndex;
180    }
181    //dump_block(block, "Add cont header");
182 }
183
184 /*
185  */
186 static bool write_data_to_block(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
187 {
188    Dmsg0(dbgep, "=== wpath 24 write_data_to_block\n");
189    rec->remlen = block->buf_len - block->binbuf;
190    /* Write as much of data as possible */
191    if (rec->remlen >= rec->remainder) {
192       Dmsg0(dbgep, "=== wpath 25 write_data_to_block\n");
193       memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
194              rec->remainder);
195       block->bufp += rec->remainder;
196       block->binbuf += rec->remainder;
197       rec->remainder = 0;
198    } else {
199       Dmsg0(dbgep, "=== wpath 26 write_data_to_block\n");
200       memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
201              rec->remlen);
202       block->bufp += rec->remlen;
203       block->binbuf += rec->remlen;
204       rec->remainder -= rec->remlen;
205       return false;                /* did partial transfer */
206    }
207    return true;
208 }
209
210 /*
211  * Write a record to the block -- handles writing out full
212  *   blocks by writing them to the device.
213  *
214  *  Returns: false means the block could not be written to tape/disk.
215  *           true  on success (all bytes written to the block).
216  */
217 bool DCR::write_record(DEV_RECORD *rec)
218 {
219    Enter(dbgel);
220    Dmsg0(dbgep, "=== wpath 33 write_record\n");
221    while (!write_record_to_block(this, rec)) {
222       Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
223                  rec->remainder);
224       if (jcr->is_canceled()) {
225          Leave(dbgel);
226          return false;
227       }
228       if (!write_block_to_device()) {
229          Dmsg0(dbgep, "=== wpath 34 write_record\n");
230          Pmsg2(000, "Got write_block_to_dev error on device %s. %s\n",
231             dev->print_name(), dev->bstrerror());
232          Leave(dbgel);
233          return false;
234       }
235       Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
236                  rec->remainder);
237    }
238    Leave(dbgel);
239    return true;
240 }
241
242 /*
243  * Write a record to the block
244  *
245  *  Returns: false on failure (none or partially written)
246  *           true  on success (all bytes written)
247  *
248  *  and remainder returned in packet.
249  *
250  *  We require enough room for the header, and we deal with
251  *  two special cases. 1. Only part of the record may have
252  *  been transferred the last time (when remainder is
253  *  non-zero), and 2. The remaining bytes to write may not
254  *  all fit into the block.
255  *
256  */
257 bool write_record_to_block(DCR *dcr, DEV_RECORD *rec)
258 {
259    char buf1[100], buf2[100];
260    bool rtn;
261
262    Enter(dbgel);
263    Dmsg0(dbgep, "=== wpath 35 enter write_record_to_block\n");
264    Dmsg7(200, "write_record_to_block() state=%d FI=%s SessId=%d"
265          " Strm=%s len=%d rem=%d remainder=%d\n", rec->wstate,
266          FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
267          stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
268          rec->remlen, rec->remainder);
269    Dmsg4(160, "write_rec Strm=%s len=%d rem=%d remainder=%d\n",
270          stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
271          rec->remlen, rec->remainder);
272
273    for ( ;; ) {
274       Dmsg0(dbgep, "=== wpath 37 top of for loop\n");
275       ASSERT(dcr->block->binbuf == (uint32_t) (dcr->block->bufp - dcr->block->buf));
276       ASSERT(dcr->block->buf_len >= dcr->block->binbuf);
277
278       switch (rec->wstate) {
279       case st_none:
280          Dmsg0(dbgep, "=== wpath 38 st_none\n");
281          /* Figure out what to do */
282          rec->wstate = st_header;
283          if (rec->FileIndex < 0) {
284             /* Label record  */
285             rec->wstate = st_header;
286             continue;
287          }
288          continue;              /* go to next state */
289
290       case st_header:
291          /*
292           * Write header
293           *
294           * If rec->remainder is non-zero, we have been called a
295           *  second (or subsequent) time to finish writing a record
296           *  that did not previously fit into the block.
297           */
298          Dmsg0(dbgep, "=== wpath 42 st_header\n");
299          if (!write_header_to_block(dcr, dcr->block, rec)) {
300             Dmsg0(dbgep, "=== wpath 43 st_header\n");
301             rec->wstate = st_cont_header;
302             goto fail_out;
303          }
304          Dmsg0(dbgep, "=== wpath 44 st_header\n");
305          rec->wstate = st_data;
306          continue;
307
308       case st_cont_header:
309          Dmsg0(dbgep, "=== wpath 45 st_cont_header\n");
310          write_continue_header_to_block(dcr, dcr->block, rec);
311          rec->wstate = st_data;
312          if (rec->remlen == 0) {
313             Dmsg0(dbgep, "=== wpath 46 st_cont_header\n");
314             goto fail_out;
315          }
316          continue;
317
318       /*
319        * We come here only once for each record
320        */
321       case st_data:
322          /*
323           * Write data
324           *
325           * Part of it may have already been transferred, and we
326           * may not have enough room to transfer the whole this time.
327           */
328          Dmsg0(dbgep, "=== wpath 47 st_data\n");
329          if (rec->remainder > 0) {
330             Dmsg0(dbgep, "=== wpath 48 st_data\n");
331             if (!write_data_to_block(dcr, dcr->block, rec)) {
332                Dmsg0(dbgep, "=== wpath 49 st_data\n");
333                rec->wstate = st_cont_header;
334                goto fail_out;
335             }
336          }
337          rec->remainder = 0;                /* did whole transfer */
338          rec->wstate = st_none;
339          goto get_out;
340
341       default:
342          Dmsg0(dbgep, "=== wpath 67!!!! default\n");
343          Dmsg0(50, "Something went wrong. Default state.\n");
344          rec->wstate = st_none;
345          goto get_out;
346       }
347    }
348 get_out:
349    rtn = true;
350    goto out;
351 fail_out:
352    rtn = false;
353 out:
354    Leave(dbgel);
355    return rtn;
356 }