]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/record_write.c
Big backport from Enterprise
[bacula/bacula] / bacula / src / stored / record_write.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 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  *              added aligned format November MMXII
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  * Write an ameta (normal) header record to the block.
48  */
49 static bool write_header_to_block(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
50 {
51    ser_declare;
52
53    Dmsg0(dbgep, "=== wpath 11 write_header_to_block\n");
54    ASSERT2(!block->adata, "Attempt to write header to adata block!");
55    rec->remlen = block->buf_len - block->binbuf;
56    /* Require enough room to write a full header */
57    if (rec->remlen < WRITE_RECHDR_LENGTH) {
58       Dmsg0(dbgep, "=== wpath 12 write_header_to_block\n");
59       Dmsg5(190, "remlen<WRITE_RECHDR_LEN adata=%d remlen=%d<%d reclen buf_len=%d binbuf=%d\n",
60          block->adata, rec->remlen, WRITE_RECHDR_LENGTH, block->buf_len, block->binbuf);
61       rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
62       return false;
63    }
64    ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
65    if (BLOCK_VER == 1) {
66       Dmsg0(dbgep, "=== wpath 13 write_header_to_block\n");
67       ser_uint32(rec->VolSessionId);
68       ser_uint32(rec->VolSessionTime);
69    } else {
70       Dmsg0(dbgep, "=== wpath 14 write_header_to_block\n");
71       block->VolSessionId = rec->VolSessionId;
72       block->VolSessionTime = rec->VolSessionTime;
73    }
74    ser_int32(rec->FileIndex);
75    ser_int32(rec->Stream);
76    ser_uint32(rec->data_len);
77
78    block->bufp += WRITE_RECHDR_LENGTH;
79    block->binbuf += WRITE_RECHDR_LENGTH;
80
81    block->RecNum++;
82    rec->remlen -= WRITE_RECHDR_LENGTH;
83    rec->remainder = rec->data_len;
84    if (rec->FileIndex > 0) {
85       Dmsg0(dbgep, "=== wpath 15 write_header_to_block\n");
86       /* If data record, update what we have in this block */
87       if (block->FirstIndex == 0) {
88          Dmsg0(dbgep, "=== wpath 16 write_header_to_block\n");
89          block->FirstIndex = rec->FileIndex;
90       }
91       block->LastIndex = rec->FileIndex;
92    }
93
94    //dump_block(dcr->dev, block, "Add header");
95    return true;
96 }
97
98 /*
99  * If the prior ameta block was not big enough to hold the
100  *  whole record, write a continuation header record.
101  */
102 static void write_continue_header_to_block(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
103 {
104    ser_declare;
105
106    Dmsg0(dbgep, "=== wpath 17 write_cont_hdr_to_block\n");
107    ASSERT2(!block->adata, "Attempt to write adata header!");
108    rec->remlen = block->buf_len - block->binbuf;
109
110    /* No space left to write the continue header */
111    if (rec->remlen == 0) {
112       return;
113    }
114
115    /*
116     * We have unwritten bytes from a previous
117     * time. Presumably we have a new buffer (possibly
118     * containing a volume label), so the new header
119     * should be able to fit in the block -- otherwise we have
120     * an error.  Note, we have to continue splitting the
121     * data record if it is longer than the block.
122     *
123     * First, write the header.
124     *
125     * Every time we write a header and it is a continuation
126     * of a previous partially written record, we store the
127     * Stream as -Stream in the record header.
128     */
129    ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
130    if (BLOCK_VER == 1) {
131       Dmsg0(dbgep, "=== wpath 18 write_cont_hdr_to_block\n");
132       ser_uint32(rec->VolSessionId);
133       ser_uint32(rec->VolSessionTime);
134    } else {
135       Dmsg0(dbgep, "=== wpath 19 write_cont_hdr_to_block\n");
136       block->VolSessionId = rec->VolSessionId;
137       block->VolSessionTime = rec->VolSessionTime;
138    }
139    ser_int32(rec->FileIndex);
140    if (rec->remainder > rec->data_len) {
141       Dmsg0(dbgep, "=== wpath 20 write_cont_hdr_to_block\n");
142       ser_int32(rec->Stream);      /* normal full header */
143       ser_uint32(rec->data_len);
144       rec->remainder = rec->data_len; /* must still do data record */
145    } else {
146       Dmsg0(dbgep, "=== wpath 21 write_cont_hdr_to_block\n");
147       ser_int32(-rec->Stream);     /* mark this as a continuation record */
148       ser_uint32(rec->remainder);  /* bytes to do */
149    }
150
151    /* Require enough room to write a full header */
152    ASSERT(rec->remlen >= WRITE_RECHDR_LENGTH);
153
154    block->bufp += WRITE_RECHDR_LENGTH;
155    block->binbuf += WRITE_RECHDR_LENGTH;
156    rec->remlen -= WRITE_RECHDR_LENGTH;
157    if (rec->FileIndex > 0) {
158       Dmsg0(dbgep, "=== wpath 22 write_cont_hdr_to_block\n");
159       /* If data record, update what we have in this block */
160       if (block->FirstIndex == 0) {
161          Dmsg0(dbgep, "=== wpath 23 write_cont_hdr_to_block\n");
162          block->FirstIndex = rec->FileIndex;
163       }
164       block->LastIndex = rec->FileIndex;
165    }
166    if (block->adata) {
167       Dmsg3(150, "=== write_cont_hdr ptr=%p begin=%p off=%d\n", block->bufp,
168          block->buf, block->bufp-block->buf);
169    }
170    block->RecNum++;
171    //dump_block(dcr->dev, block, "Add cont header");
172 }
173
174 /*
175  * Now write non-aligned data to an ameta block
176  */
177 static bool write_data_to_block(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
178 {
179    Dmsg0(dbgep, "=== wpath 24 write_data_to_block\n");
180    ASSERT2(!block->adata, "Attempt to write adata to metadata file!");
181    rec->remlen = block->buf_len - block->binbuf;
182    /* Write as much of data as possible */
183    if (rec->remlen >= rec->remainder) {
184       Dmsg0(dbgep, "=== wpath 25 write_data_to_block\n");
185       memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
186              rec->remainder);
187       block->bufp += rec->remainder;
188       block->binbuf += rec->remainder;
189       rec->remainder = 0;
190    } else {
191       if (rec->state_bits & REC_NO_SPLIT) {
192          return false;                 /* do not split record */
193       }
194       Dmsg0(dbgep, "=== wpath 26 write_data_to_block\n");
195       memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
196              rec->remlen);
197       block->bufp += rec->remlen;
198       block->binbuf += rec->remlen;
199       rec->remainder -= rec->remlen;
200       return false;                /* did partial transfer */
201    }
202    if (block->adata) {
203       /* Adata label data */
204       Dmsg3(190, "write_data adata=%d blkAddr=%lld off=%d\n",
205          block->adata, block->BlockAddr, block->bufp-block->buf);
206    }
207    //dump_block(dcr->dev, block, "Add data/adata");
208    return true;
209 }
210
211 /*
212  * Write a record to the block -- handles writing out full
213  *   blocks by writing them to the device.
214  *
215  *  Returns: false means the block could not be written to tape/disk.
216  *           true  on success (all bytes written to the block).
217  */
218 bool DCR::write_record(DEV_RECORD *rec)
219 {
220    Enter(dbgel);
221    Dmsg0(dbgep, "=== wpath 33 write_record\n");
222    while (!write_record_to_block(this, rec)) {
223       Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
224                  rec->remainder);
225       if (jcr->is_canceled()) {
226          Leave(dbgel);
227          return false;
228       }
229       if (!write_block_to_device()) {
230          Dmsg0(dbgep, "=== wpath 34 write_record\n");
231          Pmsg2(000, "Got write_block_to_dev error on device %s. %s\n",
232             dev->print_name(), dev->bstrerror());
233          Leave(dbgel);
234          return false;
235       }
236       Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
237                  rec->remainder);
238    }
239    Leave(dbgel);
240    return true;
241 }
242
243 /*
244  * Write a record to the block
245  *
246  *  Returns: false on failure (none or partially written)
247  *           true  on success (all bytes written)
248  *
249  *  and remainder returned in packet.
250  *
251  *  We require enough room for the header, and we deal with
252  *  two special cases. 1. Only part of the record may have
253  *  been transferred the last time (when remainder is
254  *  non-zero), and 2. The remaining bytes to write may not
255  *  all fit into the block.
256  *
257  *  Ensure we leave with same ameta/adata set as enter.
258  */
259 bool write_record_to_block(DCR *dcr, DEV_RECORD *rec)
260 {
261    char buf1[100], buf2[100];
262    bool save_adata = dcr->block->adata;
263    bool rtn;
264
265    Enter(dbgel);
266    Dmsg0(dbgep, "=== wpath 35 enter write_record_to_block\n");
267    Dmsg7(250, "write_record_to_block() state=%d FI=%s SessId=%d"
268          " Strm=%s len=%d rem=%d remainder=%d\n", rec->wstate,
269          FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
270          stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
271          rec->remlen, rec->remainder);
272    Dmsg4(250, "write_rec Strm=%s len=%d rem=%d remainder=%d\n",
273          stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
274          rec->remlen, rec->remainder);
275
276    if (!dcr->dev->do_pre_write_checks(dcr, rec)) {
277       goto fail_out;
278    }
279
280    for ( ;; ) {
281       Dmsg0(dbgep, "=== wpath 37 top of for loop\n");
282       ASSERT(dcr->block->binbuf == (uint32_t) (dcr->block->bufp - dcr->block->buf));
283       ASSERT(dcr->block->buf_len >= dcr->block->binbuf);
284
285       switch (rec->wstate) {
286       case st_none:
287          Dmsg0(dbgep, "=== wpath 38 st_none\n");
288          /* Figure out what to do */
289          rec->wstate = st_header;
290          /* If labeling adata, special path */
291          if (dcr->adata_label) {
292             Dmsg1(dbgep, "=== wpath adata_label set adata=%d\n", dcr->dev->adata);
293             rec->wstate = st_adata_label;
294             continue;
295          }
296          if (rec->FileIndex < 0) {
297             /* Label record -- ameta label */
298             Dmsg3(dbgep, "=== wpath label adata=%d Strm=%d FI=%d\n",
299               dcr->dev->adata, rec->Stream, rec->FileIndex);
300             rec->wstate = st_header;
301             continue;
302          }
303
304          dcr->dev->select_data_stream(dcr, rec);
305          continue;              /* go to next state */
306
307       case st_header:
308          /*
309           * Write header
310           *
311           * If rec->remainder is non-zero, we have been called a
312           *  second (or subsequent) time to finish writing a record
313           *  that did not previously fit into the block.
314           */
315          Dmsg0(dbgep, "=== wpath 42 st_header\n");
316          dcr->set_ameta();
317          if (!write_header_to_block(dcr, dcr->ameta_block, rec)) {
318             Dmsg0(dbgep, "=== wpath 43 st_header\n");
319             rec->wstate = st_cont_header;
320             goto fail_out;
321          }
322          Dmsg0(dbgep, "=== wpath 44 st_header\n");
323          rec->wstate = st_data;
324          continue;
325
326       case st_cont_header:
327          Dmsg0(dbgep, "=== wpath 45 st_cont_header\n");
328          dcr->set_ameta();
329          write_continue_header_to_block(dcr, dcr->ameta_block, rec);
330          rec->wstate = st_data;
331          if (rec->remlen == 0) {
332             Dmsg0(dbgep, "=== wpath 46 st_cont_header\n");
333             goto fail_out;
334          }
335          continue;
336
337       /*
338        * We come here only once for each record
339        */
340       case st_data:
341          /*
342           * Write data
343           *
344           * Part of it may have already been transferred, and we
345           * may not have enough room to transfer the whole this time.
346           */
347          Dmsg0(dbgep, "=== wpath 47 st_data\n");
348          dcr->set_ameta();
349          if (rec->remainder > 0) {
350             Dmsg0(dbgep, "=== wpath 48 st_data\n");
351             if (!write_data_to_block(dcr, dcr->ameta_block, rec)) {
352                Dmsg0(dbgep, "=== wpath 49 st_data\n");
353                if (rec->state_bits & REC_NO_SPLIT) {
354                   rec->wstate = st_header;
355                } else {
356                   rec->wstate = st_cont_header;
357                }
358                goto fail_out;
359             }
360          }
361          rec->state_bits &= ~REC_NO_SPLIT;  /* clear possible no split bit */
362          rec->remainder = 0;                /* did whole transfer */
363          rec->wstate = st_none;
364          goto get_out;
365
366       case st_adata_label:
367          if (!dcr->dev->write_adata_label(dcr, rec)) {
368             goto fail_out;
369          }
370          goto get_out;
371
372       /*
373        * We come here only once for each record
374        */
375       case st_adata:
376          dcr->dev->write_adata(dcr, rec);
377          continue;
378
379       case st_cont_adata:
380          dcr->dev->write_cont_adata(dcr, rec);
381          continue;
382
383       /*
384        * Note, the following two cases are handled differently
385        *  in write_adata_record_header() so take care if you want to
386        *  eliminate one of them.
387        */
388       case st_cont_adata_rechdr:
389          Dmsg2(200, "=== cont rechdr remainder=%d reclen=%d\n", rec->remainder, dcr->adata_block->reclen);
390          Dmsg0(200, "st_cont_adata_rechdr\n");
391          /* Fall through wanted */
392       case st_adata_rechdr:
393          switch(dcr->dev->write_adata_rechdr(dcr, rec)) {
394          case -1:
395             goto fail_out;
396          case 0:
397             continue;
398          case 1:
399             goto get_out;
400          }
401          break;
402
403       default:
404          Dmsg0(dbgep, "=== wpath 67!!!! default\n");
405          Dmsg0(50, "Something went wrong. Default state.\n");
406          rec->wstate = st_none;
407          goto get_out;
408       }
409    }
410 get_out:
411    rtn = true;
412    goto out;
413 fail_out:
414    rtn = false;
415 out:
416    if (save_adata) {
417       dcr->set_adata();
418    } else {
419       dcr->set_ameta();
420    }
421    Leave(dbgel);
422    return rtn;
423 }