]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/append.c
Fix SD Vol+dev lock race bug
[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 && !job_canceled(jcr); ) {
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 && !job_canceled(jcr)) {
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 || stream == STREAM_UNIX_ATTRIBUTES_EX ||
217              crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
218             if (!jcr->no_attributes) {
219                BSOCK *dir = jcr->dir_bsock;
220                if (are_attributes_spooled(jcr)) {
221                   dir->set_spooling();
222                }
223                Dmsg0(850, "Send attributes to dir.\n");
224                if (!dir_update_file_attributes(dcr, &rec)) {
225                   dir->clear_spooling();
226                   Jmsg(jcr, M_FATAL, 0, _("Error updating file attributes. ERR=%s\n"),
227                      dir->bstrerror());
228                   ok = false;
229                   break;
230                }
231                dir->clear_spooling();
232             }
233          }
234          Dmsg0(650, "Enter bnet_get\n");
235       }
236       Dmsg1(650, "End read loop with FD. Stat=%d\n", n);
237
238       if (fd->is_error()) {
239          if (!job_canceled(jcr)) {
240             Dmsg1(350, "Network read error from FD. ERR=%s\n", fd->bstrerror());
241             Jmsg1(jcr, M_FATAL, 0, _("Network error reading from FD. ERR=%s\n"),
242                   fd->bstrerror());
243          }
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    if (ok) {
253       /* Terminate connection with FD */
254       fd->fsend(OK_append);
255       do_fd_commands(jcr);               /* finish dialog with FD */
256    } else {
257       fd->fsend("3999 Failed append\n");
258    }
259
260    /*
261     * Don't use time_t for job_elapsed as time_t can be 32 or 64 bits,
262     *   and the subsequent Jmsg() editing will break
263     */
264    int32_t job_elapsed = time(NULL) - jcr->run_time;
265
266    if (job_elapsed <= 0) {
267       job_elapsed = 1;
268    }
269
270    Jmsg(dcr->jcr, M_INFO, 0, _("Job write elapsed time = %02d:%02d:%02d, Transfer rate = %s Bytes/second\n"),
271          job_elapsed / 3600, job_elapsed % 3600 / 60, job_elapsed % 60,
272          edit_uint64_with_suffix(jcr->JobBytes / job_elapsed, ec));
273
274
275    Dmsg1(200, "Write EOS label JobStatus=%c\n", jcr->JobStatus);
276
277    /*
278     * Check if we can still write. This may not be the case
279     *  if we are at the end of the tape or we got a fatal I/O error.
280     */
281    if (ok || dev->can_write()) {
282       if (!write_session_label(dcr, EOS_LABEL)) {
283          /* Print only if ok and not cancelled to avoid spurious messages */
284          if (ok && !job_canceled(jcr)) {
285             Jmsg1(jcr, M_FATAL, 0, _("Error writing end session label. ERR=%s\n"),
286                   dev->bstrerror());
287          }
288          set_jcr_job_status(jcr, JS_ErrorTerminated);
289          ok = false;
290       }
291       if (dev->VolCatInfo.VolCatName[0] == 0) {
292          Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
293          Dmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
294       }
295       Dmsg0(90, "back from write_end_session_label()\n");
296       /* Flush out final partial block of this session */
297       if (!write_block_to_device(dcr)) {
298          /* Print only if ok and not cancelled to avoid spurious messages */
299          if (ok && !job_canceled(jcr)) {
300             Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
301                   dev->print_name(), dev->bstrerror());
302             Dmsg0(100, _("Set ok=FALSE after write_block_to_device.\n"));
303          }
304          set_jcr_job_status(jcr, JS_ErrorTerminated);
305          ok = false;
306       }
307       if (dev->VolCatInfo.VolCatName[0] == 0) {
308          Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
309          Dmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
310       }
311    }
312
313
314    if (!ok) {
315       discard_data_spool(dcr);
316    } else {
317       /* Note: if commit is OK, the device will remain blocked */
318       commit_data_spool(dcr);
319    }
320
321    if (ok) {
322       ok = dvd_close_job(dcr);  /* do DVD cleanup if any */
323    }
324    
325    /*
326     * Release the device -- and send final Vol info to DIR
327     *  and unlock it.
328     */
329    release_device(dcr);
330
331    if (!ok || job_canceled(jcr)) {
332       discard_attribute_spool(jcr);
333    } else {
334       commit_attribute_spool(jcr);
335    }
336
337    dir_send_job_status(jcr);          /* update director */
338
339    Dmsg1(100, "return from do_append_data() ok=%d\n", ok);
340    return ok;
341 }