]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/append.c
Allow restore from multiple storage
[bacula/bacula] / bacula / src / stored / append.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2009 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 two of the GNU 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 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  *  Version $Id$
33  */
34
35 #include "bacula.h"
36 #include "stored.h"
37
38
39 /* Responses sent to the File daemon */
40 static char OK_data[]    = "3000 OK data\n";
41 static char OK_append[]  = "3000 OK append data\n";
42
43 /* Forward referenced functions */
44
45 /*
46  *  Append Data sent from File daemon
47  *
48  */
49 bool do_append_data(JCR *jcr)
50 {
51    int32_t n;
52    int32_t file_index, stream, last_file_index;
53    BSOCK *ds;
54    BSOCK *fd_sock = jcr->file_bsock;
55    bool ok = true;
56    DEV_RECORD rec;
57    char buf1[100], buf2[100];
58    DCR *dcr = jcr->dcr;
59    DEVICE *dev;
60    char ec[50];
61
62
63    if (!dcr) { 
64       Jmsg0(jcr, M_FATAL, 0, _("DCR is NULL!!!\n"));
65       return false;
66    }                                              
67    dev = dcr->dev;
68    if (!dev) { 
69       Jmsg0(jcr, M_FATAL, 0, _("DEVICE is NULL!!!\n"));
70       return false;
71    }                                              
72
73    Dmsg1(100, "Start append data. res=%d\n", dev->num_reserved());
74
75    memset(&rec, 0, sizeof(rec));
76
77    ds = fd_sock;
78
79    if (!ds->set_buffer_size(dcr->device->max_network_buffer_size, BNET_SETBUF_WRITE)) {
80       set_jcr_job_status(jcr, JS_ErrorTerminated);
81       Jmsg0(jcr, M_FATAL, 0, _("Unable to set network buffer size.\n"));
82       return false;
83    }
84
85    if (!acquire_device_for_append(dcr)) {
86       set_jcr_job_status(jcr, JS_ErrorTerminated);
87       return false;
88    }
89
90    set_jcr_job_status(jcr, JS_Running);
91    dir_send_job_status(jcr);
92
93    if (dev->VolCatInfo.VolCatName[0] == 0) {
94       Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
95    }
96    Dmsg1(50, "Begin append device=%s\n", dev->print_name());
97
98    begin_data_spool(dcr);
99    begin_attribute_spool(jcr);
100
101    Dmsg0(100, "Just after acquire_device_for_append\n");
102    if (dev->VolCatInfo.VolCatName[0] == 0) {
103       Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
104    }
105    /*
106     * Write Begin Session Record
107     */
108    if (!write_session_label(dcr, SOS_LABEL)) {
109       Jmsg1(jcr, M_FATAL, 0, _("Write session label failed. ERR=%s\n"),
110          dev->bstrerror());
111       set_jcr_job_status(jcr, JS_ErrorTerminated);
112       ok = false;
113    }
114    if (dev->VolCatInfo.VolCatName[0] == 0) {
115       Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
116    }
117
118    /* Tell File daemon to send data */
119    if (!fd_sock->fsend(OK_data)) {
120       berrno be;
121       Jmsg1(jcr, M_FATAL, 0, _("Network send error to FD. ERR=%s\n"),
122             be.bstrerror(fd_sock->b_errno));
123       ok = false;
124    }
125
126    /*
127     * Get Data from File daemon, write to device.  To clarify what is
128     *   going on here.  We expect:
129     *     - A stream header
130     *     - Multiple records of data
131     *     - EOD record
132     *
133     *    The Stream header is just used to sychronize things, and
134     *    none of the stream header is written to tape.
135     *    The Multiple records of data, contain first the Attributes,
136     *    then after another stream header, the file data, then
137     *    after another stream header, the MD5 data if any.
138     *
139     *   So we get the (stream header, data, EOD) three time for each
140     *   file. 1. for the Attributes, 2. for the file data if any,
141     *   and 3. for the MD5 if any.
142     */
143    dcr->VolFirstIndex = dcr->VolLastIndex = 0;
144    jcr->run_time = time(NULL);              /* start counting time for rates */
145    for (last_file_index = 0; ok && !job_canceled(jcr); ) {
146
147       /* Read Stream header from the File daemon.
148        *  The stream header consists of the following:
149        *    file_index (sequential Bacula file index, base 1)
150        *    stream     (Bacula number to distinguish parts of data)
151        *    info       (Info for Storage daemon -- compressed, encryped, ...)
152        *       info is not currently used, so is read, but ignored!
153        */
154      if ((n=bget_msg(ds)) <= 0) {
155          if (n == BNET_SIGNAL && ds->msglen == BNET_EOD) {
156             break;                    /* end of data */
157          }
158          Jmsg1(jcr, M_FATAL, 0, _("Error reading data header from FD. ERR=%s\n"),
159                ds->bstrerror());
160          ok = false;
161          break;
162       }
163
164       if (sscanf(ds->msg, "%ld %ld", &file_index, &stream) != 2) {
165          Jmsg1(jcr, M_FATAL, 0, _("Malformed data header from FD: %s\n"), ds->msg);
166          ok = false;
167          break;
168       }
169
170       Dmsg2(890, "<filed: Header FilInx=%d stream=%d\n", file_index, stream);
171
172       if (!(file_index > 0 && (file_index == last_file_index ||
173           file_index == last_file_index + 1))) {
174          Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
175          ok = false;
176          break;
177       }
178       if (file_index != last_file_index) {
179          jcr->JobFiles = file_index;
180          last_file_index = file_index;
181       }
182
183       /* Read data stream from the File daemon.
184        *  The data stream is just raw bytes
185        */
186       while ((n=bget_msg(ds)) > 0 && !job_canceled(jcr)) {
187          rec.VolSessionId = jcr->VolSessionId;
188          rec.VolSessionTime = jcr->VolSessionTime;
189          rec.FileIndex = file_index;
190          rec.Stream = stream;
191          rec.data_len = ds->msglen;
192          rec.data = ds->msg;            /* use message buffer */
193
194          Dmsg4(850, "before writ_rec FI=%d SessId=%d Strm=%s len=%d\n",
195             rec.FileIndex, rec.VolSessionId, 
196             stream_to_ascii(buf1, rec.Stream,rec.FileIndex),
197             rec.data_len);
198
199          while (!write_record_to_block(dcr->block, &rec)) {
200             Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
201                        rec.remainder);
202             if (!write_block_to_device(dcr)) {
203                Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
204                   dev->print_name(), dev->bstrerror());
205                ok = false;
206                break;
207             }
208          }
209          if (!ok) {
210             Dmsg0(400, "Not OK\n");
211             break;
212          }
213          jcr->JobBytes += rec.data_len;   /* increment bytes this job */
214          Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
215             FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
216             stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
217
218          /* Send attributes and digest to Director for Catalog */
219          if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX ||
220              crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
221             if (!jcr->no_attributes) {
222                if (are_attributes_spooled(jcr)) {
223                   jcr->dir_bsock->set_spooling();
224                }
225                Dmsg0(850, "Send attributes to dir.\n");
226                if (!dir_update_file_attributes(dcr, &rec)) {
227                   jcr->dir_bsock->clear_spooling();
228                   Jmsg(jcr, M_FATAL, 0, _("Error updating file attributes. ERR=%s\n"),
229                      jcr->dir_bsock->bstrerror());
230                   ok = false;
231                   break;
232                }
233                jcr->dir_bsock->clear_spooling();
234             }
235          }
236          Dmsg0(650, "Enter bnet_get\n");
237       }
238       Dmsg1(650, "End read loop with FD. Stat=%d\n", n);
239
240       if (is_bnet_error(ds)) {
241          Dmsg1(350, "Network read error from FD. ERR=%s\n", ds->bstrerror());
242          Jmsg1(jcr, M_FATAL, 0, _("Network error on data channel. ERR=%s\n"),
243                ds->bstrerror());
244          ok = false;
245          break;
246       }
247    }
248
249    /* Create Job status for end of session label */
250    set_jcr_job_status(jcr, ok?JS_Terminated:JS_ErrorTerminated);
251
252    /* Terminate connection with FD */
253    ds->fsend(OK_append);
254    do_fd_commands(jcr);               /* finish dialog with FD */
255
256    /*
257     * Don't use time_t for job_elapsed as time_t can be 32 or 64 bits,
258     *   and the subsequent Jmsg() editing will break
259     */
260    int32_t job_elapsed = time(NULL) - jcr->run_time;
261
262    if (job_elapsed <= 0) {
263       job_elapsed = 1;
264    }
265
266    Jmsg(dcr->jcr, M_INFO, 0, _("Job write elapsed time = %02d:%02d:%02d, Transfer rate = %s bytes/second\n"),
267          job_elapsed / 3600, job_elapsed % 3600 / 60, job_elapsed % 60,
268          edit_uint64_with_suffix(jcr->JobBytes / job_elapsed, ec));
269
270
271    Dmsg1(200, "Write EOS label JobStatus=%c\n", jcr->JobStatus);
272
273    /*
274     * Check if we can still write. This may not be the case
275     *  if we are at the end of the tape or we got a fatal I/O error.
276     */
277    if (ok || dev->can_write()) {
278       if (!write_session_label(dcr, EOS_LABEL)) {
279          Jmsg1(jcr, M_FATAL, 0, _("Error writting end session label. ERR=%s\n"),
280                dev->bstrerror());
281          set_jcr_job_status(jcr, JS_ErrorTerminated);
282          ok = false;
283       }
284       if (dev->VolCatInfo.VolCatName[0] == 0) {
285          Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
286          Dmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
287       }
288       Dmsg0(90, "back from write_end_session_label()\n");
289       /* Flush out final partial block of this session */
290       if (!write_block_to_device(dcr)) {
291          Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
292                dev->print_name(), dev->bstrerror());
293          Dmsg0(100, _("Set ok=FALSE after write_block_to_device.\n"));
294          ok = false;
295       }
296       if (dev->VolCatInfo.VolCatName[0] == 0) {
297          Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
298          Dmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
299       }
300    }
301
302
303    if (!ok) {
304       discard_data_spool(dcr);
305    } else {
306       /* Note: if commit is OK, the device will remain locked */
307       commit_data_spool(dcr);
308    }
309
310    if (ok) {
311       ok = dvd_close_job(dcr);  /* do DVD cleanup if any */
312    }
313    
314    /*
315     * Release the device -- and send final Vol info to DIR
316     *  and unlock it.
317     */
318    release_device(dcr);
319
320    if (!ok || job_canceled(jcr)) {
321       discard_attribute_spool(jcr);
322    } else {
323       commit_attribute_spool(jcr);
324    }
325
326    dir_send_job_status(jcr);          /* update director */
327
328    Dmsg1(100, "return from do_append_data() ok=%d\n", ok);
329    return ok;
330 }