]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/append.c
Rename incomplete to rerunning for clarity
[bacula/bacula] / bacula / src / stored / append.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  * Append code for Storage daemon
30  *  Kern Sibbald, May MM
31  *
32  */
33
34 #include "bacula.h"
35 #include "stored.h"
36
37
38 /* Responses sent to the File daemon */
39 static char OK_data[]    = "3000 OK data\n";
40 static char OK_append[]  = "3000 OK append data\n";
41
42 /* Forward referenced functions */
43
44
45 /* 
46  * Check if we can mark this job incomplete
47  *
48  */
49 void possible_incomplete_job(JCR *jcr, int32_t last_file_index)
50 {
51    /*
52     * Note, here we decide if it is worthwhile to restart
53     *  the Job at this point. For the moment, if at least
54     *  10 Files have been seen, which is good for testing, but
55     *  for a production system, we probably want something like
56     *  100-1000 files, and some number of bytes of data.
57     *
58     *  ****FIXME**** set last_file_index to something more
59     *    reasonable, and maybe check JobBytes.
60     */
61    if (jcr->spool_attributes && last_file_index > 10) {
62       jcr->setJobStatus(JS_Incomplete);
63    }
64 }
65 /*
66  *  Append Data sent from File daemon
67  *
68  */
69 bool do_append_data(JCR *jcr)
70 {
71    int32_t n;
72    int32_t file_index, stream, last_file_index;
73    BSOCK *fd = jcr->file_bsock;
74    bool ok = true;
75    DEV_RECORD rec;
76    char buf1[100], buf2[100];
77    DCR *dcr = jcr->dcr;
78    DEVICE *dev;
79    char ec[50];
80
81
82    if (!dcr) { 
83       Jmsg0(jcr, M_FATAL, 0, _("DCR is NULL!!!\n"));
84       return false;
85    }                                              
86    dev = dcr->dev;
87    if (!dev) { 
88       Jmsg0(jcr, M_FATAL, 0, _("DEVICE is NULL!!!\n"));
89       return false;
90    }                                              
91
92    Dmsg1(100, "Start append data. res=%d\n", dev->num_reserved());
93
94    memset(&rec, 0, sizeof(rec));
95
96    if (!fd->set_buffer_size(dcr->device->max_network_buffer_size, BNET_SETBUF_WRITE)) {
97       jcr->setJobStatus(JS_ErrorTerminated);
98       Jmsg0(jcr, M_FATAL, 0, _("Unable to set network buffer size.\n"));
99       return false;
100    }
101
102    if (!acquire_device_for_append(dcr)) {
103       jcr->setJobStatus(JS_ErrorTerminated);
104       return false;
105    }
106
107    jcr->setJobStatus(JS_Running);
108    dir_send_job_status(jcr);
109
110    if (dev->VolCatInfo.VolCatName[0] == 0) {
111       Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
112    }
113    Dmsg1(50, "Begin append device=%s\n", dev->print_name());
114
115    begin_data_spool(dcr);
116    begin_attribute_spool(jcr);
117
118    Dmsg0(100, "Just after acquire_device_for_append\n");
119    if (dev->VolCatInfo.VolCatName[0] == 0) {
120       Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
121    }
122    /*
123     * Write Begin Session Record
124     */
125    if (!write_session_label(dcr, SOS_LABEL)) {
126       Jmsg1(jcr, M_FATAL, 0, _("Write session label failed. ERR=%s\n"),
127          dev->bstrerror());
128       jcr->setJobStatus(JS_ErrorTerminated);
129       ok = false;
130    }
131    if (dev->VolCatInfo.VolCatName[0] == 0) {
132       Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
133    }
134
135    /* Tell File daemon to send data */
136    if (!fd->fsend(OK_data)) {
137       berrno be;
138       Jmsg1(jcr, M_FATAL, 0, _("Network send error to FD. ERR=%s\n"),
139             be.bstrerror(fd->b_errno));
140       ok = false;
141    }
142
143    /*
144     * Get Data from File daemon, write to device.  To clarify what is
145     *   going on here.  We expect:
146     *     - A stream header
147     *     - Multiple records of data
148     *     - EOD record
149     *
150     *    The Stream header is just used to sychronize things, and
151     *    none of the stream header is written to tape.
152     *    The Multiple records of data, contain first the Attributes,
153     *    then after another stream header, the file data, then
154     *    after another stream header, the MD5 data if any.
155     *
156     *   So we get the (stream header, data, EOD) three time for each
157     *   file. 1. for the Attributes, 2. for the file data if any,
158     *   and 3. for the MD5 if any.
159     */
160    dcr->VolFirstIndex = dcr->VolLastIndex = 0;
161    jcr->run_time = time(NULL);              /* start counting time for rates */
162    for (last_file_index = 0; ok && !jcr->is_job_canceled(); ) {
163
164       /* Read Stream header from the File daemon.
165        *  The stream header consists of the following:
166        *    file_index (sequential Bacula file index, base 1)
167        *    stream     (Bacula number to distinguish parts of data)
168        *    info       (Info for Storage daemon -- compressed, encrypted, ...)
169        *       info is not currently used, so is read, but ignored!
170        */
171      if ((n=bget_msg(fd)) <= 0) {
172          if (n == BNET_SIGNAL && fd->msglen == BNET_EOD) {
173             break;                    /* end of data */
174          }
175          Jmsg1(jcr, M_FATAL, 0, _("Error reading data header from FD. ERR=%s\n"),
176                fd->bstrerror());
177          possible_incomplete_job(jcr, last_file_index);
178          ok = false;
179          break;
180       }
181
182       if (sscanf(fd->msg, "%ld %ld", &file_index, &stream) != 2) {
183          Jmsg1(jcr, M_FATAL, 0, _("Malformed data header from FD: %s\n"), fd->msg);
184          ok = false;
185          possible_incomplete_job(jcr, last_file_index);
186          break;
187       }
188
189       Dmsg2(890, "<filed: Header FilInx=%d stream=%d\n", file_index, stream);
190
191       /*
192        * We make sure the file_index is advancing sequentially.
193        * An incomplete job can start the file_index at any number.
194        * otherwise, it must start at 1.
195        */
196       if (jcr->rerunning && file_index > 0 && last_file_index == 0) {
197          goto fi_checked;
198       }
199       if (file_index > 0 && (file_index == last_file_index ||
200           file_index == last_file_index + 1)) {
201          goto fi_checked;
202       }
203       Jmsg2(jcr, M_FATAL, 0, _("FI=%d from FD not positive or sequential=%d\n"),
204             file_index, last_file_index);
205       possible_incomplete_job(jcr, last_file_index);
206       ok = false;
207       break;
208
209 fi_checked:
210       if (file_index != last_file_index) {
211          jcr->JobFiles = file_index;
212          last_file_index = file_index;
213       }
214
215       /* Read data stream from the File daemon.
216        *  The data stream is just raw bytes
217        */
218       while ((n=bget_msg(fd)) > 0 && !jcr->is_job_canceled()) {
219          rec.VolSessionId = jcr->VolSessionId;
220          rec.VolSessionTime = jcr->VolSessionTime;
221          rec.FileIndex = file_index;
222          rec.Stream = stream;
223          rec.maskedStream = stream & STREAMMASK_TYPE;   /* strip high bits */
224          rec.data_len = fd->msglen;
225          rec.data = fd->msg;            /* use message buffer */
226
227          Dmsg4(850, "before writ_rec FI=%d SessId=%d Strm=%s len=%d\n",
228             rec.FileIndex, rec.VolSessionId, 
229             stream_to_ascii(buf1, rec.Stream,rec.FileIndex),
230             rec.data_len);
231
232          while (!write_record_to_block(dcr->block, &rec)) {
233             Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
234                        rec.remainder);
235             if (!write_block_to_device(dcr)) {
236                Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
237                   dev->print_name(), dev->bstrerror());
238                ok = false;
239                break;
240             }
241          }
242          if (!ok) {
243             Dmsg0(400, "Not OK\n");
244             break;
245          }
246          jcr->JobBytes += rec.data_len;   /* increment bytes this job */
247          Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
248             FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
249             stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
250
251          send_attrs_to_dir(jcr, &rec);
252          Dmsg0(650, "Enter bnet_get\n");
253       }
254       Dmsg1(650, "End read loop with FD. Stat=%d\n", n);
255
256       if (fd->is_error()) {
257          if (!jcr->is_job_canceled()) {
258             Dmsg1(350, "Network read error from FD. ERR=%s\n", fd->bstrerror());
259             Jmsg1(jcr, M_FATAL, 0, _("Network error reading from FD. ERR=%s\n"),
260                   fd->bstrerror());
261             possible_incomplete_job(jcr, last_file_index);
262          }
263          ok = false;
264          break;
265       }
266    }
267
268    /* Create Job status for end of session label */
269    jcr->setJobStatus(ok?JS_Terminated:JS_ErrorTerminated);
270
271    if (ok) {
272       /* Terminate connection with FD */
273       fd->fsend(OK_append);
274       do_fd_commands(jcr);               /* finish dialog with FD */
275    } else {
276       fd->fsend("3999 Failed append\n");
277    }
278
279    /*
280     * Don't use time_t for job_elapsed as time_t can be 32 or 64 bits,
281     *   and the subsequent Jmsg() editing will break
282     */
283    int32_t job_elapsed = time(NULL) - jcr->run_time;
284
285    if (job_elapsed <= 0) {
286       job_elapsed = 1;
287    }
288
289    Jmsg(dcr->jcr, M_INFO, 0, _("Job write elapsed time = %02d:%02d:%02d, Transfer rate = %s Bytes/second\n"),
290          job_elapsed / 3600, job_elapsed % 3600 / 60, job_elapsed % 60,
291          edit_uint64_with_suffix(jcr->JobBytes / job_elapsed, ec));
292
293
294    Dmsg1(200, "Write EOS label JobStatus=%c\n", jcr->JobStatus);
295
296    /*
297     * Check if we can still write. This may not be the case
298     *  if we are at the end of the tape or we got a fatal I/O error.
299     */
300    if (ok || dev->can_write()) {
301       if (!write_session_label(dcr, EOS_LABEL)) {
302          /* Print only if ok and not cancelled to avoid spurious messages */
303          if (ok && !jcr->is_job_canceled()) {
304             Jmsg1(jcr, M_FATAL, 0, _("Error writing end session label. ERR=%s\n"),
305                   dev->bstrerror());
306             possible_incomplete_job(jcr, last_file_index);
307          }
308          jcr->setJobStatus(JS_ErrorTerminated);
309          ok = false;
310       }
311       if (dev->VolCatInfo.VolCatName[0] == 0) {
312          Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
313          Dmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
314       }
315       Dmsg0(90, "back from write_end_session_label()\n");
316       /* Flush out final partial block of this session */
317       if (!write_block_to_device(dcr)) {
318          /* Print only if ok and not cancelled to avoid spurious messages */
319          if (ok && !jcr->is_job_canceled()) {
320             Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
321                   dev->print_name(), dev->bstrerror());
322             Dmsg0(100, _("Set ok=FALSE after write_block_to_device.\n"));
323             possible_incomplete_job(jcr, last_file_index);
324          }
325          jcr->setJobStatus(JS_ErrorTerminated);
326          ok = false;
327       }
328    }
329
330
331    if (!ok && !jcr->is_JobStatus(JS_Incomplete)) {
332       discard_data_spool(dcr);
333    } else {
334       /* Note: if commit is OK, the device will remain blocked */
335       commit_data_spool(dcr);
336    }
337
338    if (ok) {
339       ok = dvd_close_job(dcr);  /* do DVD cleanup if any */
340    }
341    
342    /*
343     * Release the device -- and send final Vol info to DIR
344     *  and unlock it.
345     */
346    release_device(dcr);
347
348    if ((!ok || jcr->is_job_canceled()) && !jcr->is_JobStatus(JS_Incomplete)) {
349       discard_attribute_spool(jcr);
350    } else {
351       commit_attribute_spool(jcr);
352    }
353
354    dir_send_job_status(jcr);          /* update director */
355
356    Dmsg1(100, "return from do_append_data() ok=%d\n", ok);
357    return ok;
358 }
359
360
361 /* Send attributes and digest to Director for Catalog */
362 bool send_attrs_to_dir(JCR *jcr, DEV_RECORD *rec)
363 {
364    if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES    || 
365        rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX ||
366        rec->maskedStream == STREAM_RESTORE_OBJECT     ||
367        crypto_digest_stream_type(rec->maskedStream) != CRYPTO_DIGEST_NONE) {
368       if (!jcr->no_attributes) {
369          BSOCK *dir = jcr->dir_bsock;
370          if (are_attributes_spooled(jcr)) {
371             dir->set_spooling();
372          }
373          Dmsg0(850, "Send attributes to dir.\n");
374          if (!dir_update_file_attributes(jcr->dcr, rec)) {
375             Jmsg(jcr, M_FATAL, 0, _("Error updating file attributes. ERR=%s\n"),
376                dir->bstrerror());
377             dir->clear_spooling();
378             return false;
379          }
380          dir->clear_spooling();
381       }
382    }
383    return true;
384 }