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