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