2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
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
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.
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
23 Bacula® is a registered trademark of John Walker.
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.
29 * Append code for Storage daemon
30 * Kern Sibbald, May MM
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";
43 /* Forward referenced functions */
46 * Append Data sent from File daemon
49 bool do_append_data(JCR *jcr)
52 int32_t file_index, stream, last_file_index;
54 BSOCK *fd_sock = jcr->file_bsock;
57 char buf1[100], buf2[100];
64 Jmsg0(jcr, M_FATAL, 0, _("DCR is NULL!!!\n"));
69 Jmsg0(jcr, M_FATAL, 0, _("DEVICE is NULL!!!\n"));
73 Dmsg1(100, "Start append data. res=%d\n", dev->reserved_device);
75 memset(&rec, 0, sizeof(rec));
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"));
85 if (!acquire_device_for_append(dcr)) {
86 set_jcr_job_status(jcr, JS_ErrorTerminated);
90 set_jcr_job_status(jcr, JS_Running);
91 dir_send_job_status(jcr);
93 if (dev->VolCatInfo.VolCatName[0] == 0) {
94 Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
96 Dmsg1(50, "Begin append device=%s\n", dev->print_name());
98 begin_data_spool(dcr);
99 begin_attribute_spool(jcr);
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"));
106 * Write Begin Session Record
108 if (!write_session_label(dcr, SOS_LABEL)) {
109 Jmsg1(jcr, M_FATAL, 0, _("Write session label failed. ERR=%s\n"),
111 set_jcr_job_status(jcr, JS_ErrorTerminated);
114 if (dev->VolCatInfo.VolCatName[0] == 0) {
115 Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
118 /* Tell File daemon to send data */
119 if (!fd_sock->fsend(OK_data)) {
121 Jmsg1(jcr, M_FATAL, 0, _("Network send error to FD. ERR=%s\n"),
122 be.bstrerror(fd_sock->b_errno));
127 * Get Data from File daemon, write to device. To clarify what is
128 * going on here. We expect:
130 * - Multiple records of data
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.
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.
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); ) {
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!
154 if ((n=bget_msg(ds)) <= 0) {
155 if (n == BNET_SIGNAL && ds->msglen == BNET_EOD) {
156 break; /* end of data */
158 Jmsg1(jcr, M_FATAL, 0, _("Error reading data header from FD. ERR=%s\n"),
165 * This hand scanning is a bit more complicated than a simple
166 * sscanf, but it allows us to handle any size integer up to
167 * int64_t without worrying about whether %d, %ld, %lld, or %q
168 * is the correct format for each different architecture.
169 * It is a real pity that sscanf() is not portable.
172 while (B_ISSPACE(*p)) {
175 file_index = (int32_t)str_to_int64(p);
176 while (B_ISDIGIT(*p)) {
179 if (!B_ISSPACE(*p) || !B_ISDIGIT(*(p+1))) {
180 Jmsg1(jcr, M_FATAL, 0, _("Malformed data header from FD: %s\n"), ds->msg);
184 stream = (int32_t)str_to_int64(p);
186 Dmsg2(890, "<filed: Header FilInx=%d stream=%d\n", file_index, stream);
188 if (!(file_index > 0 && (file_index == last_file_index ||
189 file_index == last_file_index + 1))) {
190 Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
194 if (file_index != last_file_index) {
195 jcr->JobFiles = file_index;
196 last_file_index = file_index;
199 /* Read data stream from the File daemon.
200 * The data stream is just raw bytes
202 while ((n=bget_msg(ds)) > 0 && !job_canceled(jcr)) {
203 rec.VolSessionId = jcr->VolSessionId;
204 rec.VolSessionTime = jcr->VolSessionTime;
205 rec.FileIndex = file_index;
207 rec.data_len = ds->msglen;
208 rec.data = ds->msg; /* use message buffer */
210 Dmsg4(850, "before writ_rec FI=%d SessId=%d Strm=%s len=%d\n",
211 rec.FileIndex, rec.VolSessionId,
212 stream_to_ascii(buf1, rec.Stream,rec.FileIndex),
215 while (!write_record_to_block(dcr->block, &rec)) {
216 Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
218 if (!write_block_to_device(dcr)) {
219 Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
220 dev->print_name(), dev->bstrerror());
226 Dmsg0(400, "Not OK\n");
229 jcr->JobBytes += rec.data_len; /* increment bytes this job */
230 Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
231 FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
232 stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
234 /* Send attributes and digest to Director for Catalog */
235 if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX ||
236 crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
237 if (!jcr->no_attributes) {
238 if (are_attributes_spooled(jcr)) {
239 jcr->dir_bsock->set_spooling();
241 Dmsg0(850, "Send attributes to dir.\n");
242 if (!dir_update_file_attributes(dcr, &rec)) {
243 jcr->dir_bsock->clear_spooling();
244 Jmsg(jcr, M_FATAL, 0, _("Error updating file attributes. ERR=%s\n"),
245 jcr->dir_bsock->bstrerror());
249 jcr->dir_bsock->clear_spooling();
252 Dmsg0(650, "Enter bnet_get\n");
254 Dmsg1(650, "End read loop with FD. Stat=%d\n", n);
256 if (is_bnet_error(ds)) {
257 Dmsg1(350, "Network read error from FD. ERR=%s\n", ds->bstrerror());
258 Jmsg1(jcr, M_FATAL, 0, _("Network error on data channel. ERR=%s\n"),
265 /* Create Job status for end of session label */
266 set_jcr_job_status(jcr, ok?JS_Terminated:JS_ErrorTerminated);
268 /* Terminate connection with FD */
269 ds->fsend(OK_append);
270 do_fd_commands(jcr); /* finish dialog with FD */
273 time_t job_elapsed = time(NULL) - jcr->run_time;
275 if (job_elapsed <= 0) {
279 Jmsg(dcr->jcr, M_INFO, 0, _("Job write elapsed time = %02d:%02d:%02d, Transfer rate = %s bytes/second\n"),
280 job_elapsed / 3600, job_elapsed % 3600 / 60, job_elapsed % 60,
281 edit_uint64_with_suffix(jcr->JobBytes / job_elapsed, ec));
284 Dmsg1(200, "Write EOS label JobStatus=%c\n", jcr->JobStatus);
287 * Check if we can still write. This may not be the case
288 * if we are at the end of the tape or we got a fatal I/O error.
290 if (dev->can_write()) {
291 if (!write_session_label(dcr, EOS_LABEL)) {
292 Jmsg1(jcr, M_FATAL, 0, _("Error writting end session label. ERR=%s\n"),
294 set_jcr_job_status(jcr, JS_ErrorTerminated);
297 if (dev->VolCatInfo.VolCatName[0] == 0) {
298 Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
300 Dmsg0(90, "back from write_end_session_label()\n");
301 /* Flush out final partial block of this session */
302 if (!write_block_to_device(dcr)) {
303 Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
304 dev->print_name(), dev->bstrerror());
305 Dmsg0(100, _("Set ok=FALSE after write_block_to_device.\n"));
308 if (dev->VolCatInfo.VolCatName[0] == 0) {
309 Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
315 discard_data_spool(dcr);
317 /* Note: if commit is OK, the device will remain locked */
318 commit_data_spool(dcr);
322 ok = dvd_close_job(dcr); /* do DVD cleanup if any */
326 * Release the device -- and send final Vol info to DIR
331 if (!ok || job_canceled(jcr)) {
332 discard_attribute_spool(jcr);
334 commit_attribute_spool(jcr);
337 dir_send_job_status(jcr); /* update director */
339 Dmsg1(100, "return from do_append_data() ok=%d\n", ok);