1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2006-2012 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2012 Planets Communications B.V.
6    Copyright (C) 2013-2017 Bareos GmbH & Co. KG
7 
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    Affero 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 /*
24  * Kern Sibbald, January MMVI
25  */
26 /**
27  * @file
28  * responsible for doing
29  * migration, archive, copy, and virtual backup jobs.
30  */
31 
32 #include "include/bareos.h"
33 #include "stored/stored.h"
34 #include "stored/stored_globals.h"
35 #include "stored/acquire.h"
36 #include "stored/bsr.h"
37 #include "stored/append.h"
38 #include "stored/device.h"
39 #include "stored/jcr_private.h"
40 #include "stored/label.h"
41 #include "stored/mount.h"
42 #include "stored/read_record.h"
43 #include "stored/sd_stats.h"
44 #include "stored/spool.h"
45 #include "lib/bget_msg.h"
46 #include "lib/bnet.h"
47 #include "lib/bsock.h"
48 #include "lib/edit.h"
49 #include "include/jcr.h"
50 
51 namespace storagedaemon {
52 
53 /**
54  * Responses sent to the Director
55  */
56 static char Job_end[] =
57     "3099 Job %s end JobStatus=%d JobFiles=%d JobBytes=%s JobErrors=%u\n";
58 
59 /**
60  * Responses received from Storage Daemon
61  */
62 static char OK_start_replicate[] = "3000 OK start replicate ticket = %d\n";
63 static char OK_data[] = "3000 OK data\n";
64 static char OK_replicate[] = "3000 OK replicate data\n";
65 static char OK_end_replicate[] = "3000 OK end replicate\n";
66 
67 /**
68  * Commands sent to Storage Daemon
69  */
70 static char start_replicate[] = "start replicate\n";
71 static char ReplicateData[] = "replicate data %d\n";
72 static char end_replicate[] = "end replicate\n";
73 
74 
75 /* get information from first original SOS label for our job */
76 static bool found_first_sos_label = false;
77 
78 
79 /**
80  * Get response from Storage daemon to a command we sent.
81  * Check that the response is OK.
82  *
83  * Returns: false on failure
84  *          true on success
85  */
response(JobControlRecord * jcr,BareosSocket * sd,char * resp,const char * cmd)86 static bool response(JobControlRecord* jcr,
87                      BareosSocket* sd,
88                      char* resp,
89                      const char* cmd)
90 {
91   if (sd->errors) { return false; }
92   if (BgetMsg(sd) > 0) {
93     Dmsg1(110, "<stored: %s", sd->msg);
94     if (bstrcmp(sd->msg, resp)) { return true; }
95   }
96   if (JobCanceled(jcr)) {
97     return false; /* if canceled avoid useless error messages */
98   }
99   if (IsBnetError(sd)) {
100     Jmsg2(jcr, M_FATAL, 0,
101           _("Comm error with SD. bad response to %s. ERR=%s\n"), cmd,
102           BnetStrerror(sd));
103   } else {
104     Jmsg3(jcr, M_FATAL, 0, _("Bad response to %s command. Wanted %s, got %s\n"),
105           cmd, resp, sd->msg);
106   }
107   return false;
108 }
109 
110 /**
111  * Called here for each record from ReadRecords()
112  * This function is used when we do a internal clone of a Job e.g.
113  * this SD is both the reading and writing SD.
114  *
115  * Returns: true if OK
116  *           false if error
117  */
CloneRecordInternally(DeviceControlRecord * dcr,DeviceRecord * rec)118 static bool CloneRecordInternally(DeviceControlRecord* dcr, DeviceRecord* rec)
119 {
120   bool retval = false;
121   bool translated_record = false;
122   JobControlRecord* jcr = dcr->jcr;
123   Device* dev = jcr->impl->dcr->dev;
124   char buf1[100], buf2[100];
125 
126   /*
127    * If label, discard it as we create our SOS and EOS Labels
128    * However, we still want the first Start Of Session label as that contains
129    * the timestamps from the first original Job.
130    *
131    * We need to put this info in our own SOS and EOS Labels so that
132    * bscan-ing of virtual and always incremental consolidated jobs
133    * works.
134    */
135 
136   if (rec->FileIndex < 0) {
137     if (rec->FileIndex == SOS_LABEL) {
138       if (!found_first_sos_label) {
139         Dmsg0(200, "Found first SOS_LABEL and adopting job info\n");
140         found_first_sos_label = true;
141 
142         static Session_Label the_label;
143         static Session_Label* label = &the_label;
144 
145         struct date_time dt;
146         struct tm tm;
147 
148         UnserSessionLabel(label, rec);
149 
150         if (jcr->is_JobType(JT_MIGRATE) || jcr->is_JobType(JT_COPY)) {
151           bstrncpy(jcr->Job, label->Job, sizeof(jcr->Job));
152           PmStrcpy(jcr->impl->job_name, label->JobName);
153           PmStrcpy(jcr->client_name, label->ClientName);
154           PmStrcpy(jcr->impl->fileset_name, label->FileSetName);
155           PmStrcpy(jcr->impl->fileset_md5, label->FileSetMD5);
156         }
157         jcr->setJobType(label->JobType);
158         jcr->setJobLevel(label->JobLevel);
159         Dmsg1(200, "joblevel from SOS_LABEL is now %c\n", label->JobLevel);
160 
161         if (label->VerNum >= 11) {
162           jcr->sched_time = BtimeToUnix(label->write_btime);
163 
164         } else {
165           dt.julian_day_number = label->write_date;
166           dt.julian_day_fraction = label->write_time;
167           TmDecode(&dt, &tm);
168           tm.tm_isdst = 0;
169           jcr->sched_time = mktime(&tm);
170         }
171         jcr->start_time = jcr->sched_time;
172 
173         /* write the SOS Label with the existing timestamp infos */
174         if (!WriteSessionLabel(jcr->impl->dcr, SOS_LABEL)) {
175           Jmsg1(jcr, M_FATAL, 0, _("Write session label failed. ERR=%s\n"),
176                 dev->bstrerror());
177           jcr->setJobStatus(JS_ErrorTerminated);
178           retval = false;
179           goto bail_out;
180         }
181       } else {
182         Dmsg0(200, "Found additional SOS_LABEL, ignoring! \n");
183       }
184     }
185     /* Other label than SOS -> skip */
186     retval = true;
187     goto bail_out;
188   }
189 
190   /*
191    * For normal migration jobs, FileIndex values are sequential because
192    *  we are dealing with one job.  However, for Vbackup (consolidation),
193    *  we will be getting records from multiple jobs and writing them back
194    *  out, so we need to ensure that the output FileIndex is sequential.
195    *  We do so by detecting a FileIndex change and incrementing the
196    *  JobFiles, which we then use as the output FileIndex.
197    */
198   if (rec->FileIndex >= 0) {
199     /*
200      * If something changed, increment FileIndex
201      */
202     if (rec->VolSessionId != rec->last_VolSessionId ||
203         rec->VolSessionTime != rec->last_VolSessionTime ||
204         rec->FileIndex != rec->last_FileIndex) {
205       jcr->JobFiles++;
206       rec->last_VolSessionId = rec->VolSessionId;
207       rec->last_VolSessionTime = rec->VolSessionTime;
208       rec->last_FileIndex = rec->FileIndex;
209     }
210     rec->FileIndex = jcr->JobFiles; /* set sequential output FileIndex */
211   }
212 
213   /*
214    * Modify record SessionId and SessionTime to correspond to output.
215    */
216   rec->VolSessionId = jcr->VolSessionId;
217   rec->VolSessionTime = jcr->VolSessionTime;
218 
219   Dmsg5(200, "before write JobId=%d FI=%s SessId=%d Strm=%s len=%d\n",
220         jcr->JobId, FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
221         stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
222 
223   /*
224    * Perform record translations.
225    */
226   jcr->impl->dcr->before_rec = rec;
227   jcr->impl->dcr->after_rec = NULL;
228   if (GeneratePluginEvent(jcr, bsdEventWriteRecordTranslation,
229                           jcr->impl->dcr) != bRC_OK) {
230     goto bail_out;
231   }
232 
233   /*
234    * The record got translated when we got an after_rec pointer after calling
235    * the bsdEventWriteRecordTranslation plugin event. If no translation has
236    * taken place we just point the after_rec pointer to same DeviceRecord as in
237    * the before_rec pointer.
238    */
239   if (!jcr->impl->dcr->after_rec) {
240     jcr->impl->dcr->after_rec = jcr->impl->dcr->before_rec;
241   } else {
242     translated_record = true;
243   }
244 
245   while (!WriteRecordToBlock(jcr->impl->dcr, jcr->impl->dcr->after_rec)) {
246     Dmsg4(200, "!WriteRecordToBlock blkpos=%u:%u len=%d rem=%d\n", dev->file,
247           dev->block_num, jcr->impl->dcr->after_rec->data_len,
248           jcr->impl->dcr->after_rec->remainder);
249     if (!jcr->impl->dcr->WriteBlockToDevice()) {
250       Dmsg2(90, "Got WriteBlockToDev error on device %s. %s\n",
251             dev->print_name(), dev->bstrerror());
252       Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
253             dev->print_name(), dev->bstrerror());
254       goto bail_out;
255     }
256     Dmsg2(200, "===== Wrote block new pos %u:%u\n", dev->file, dev->block_num);
257   }
258 
259   /*
260    * Restore packet
261    */
262   jcr->impl->dcr->after_rec->VolSessionId =
263       jcr->impl->dcr->after_rec->last_VolSessionId;
264   jcr->impl->dcr->after_rec->VolSessionTime =
265       jcr->impl->dcr->after_rec->last_VolSessionTime;
266 
267   if (jcr->impl->dcr->after_rec->FileIndex < 0) {
268     retval = true; /* don't send LABELs to Dir */
269     goto bail_out;
270   }
271 
272   jcr->JobBytes +=
273       jcr->impl->dcr->after_rec->data_len; /* increment bytes of this job */
274 
275   Dmsg5(500, "wrote_record JobId=%d FI=%s SessId=%d Strm=%s len=%d\n",
276         jcr->JobId, FI_to_ascii(buf1, jcr->impl->dcr->after_rec->FileIndex),
277         jcr->impl->dcr->after_rec->VolSessionId,
278         stream_to_ascii(buf2, jcr->impl->dcr->after_rec->Stream,
279                         jcr->impl->dcr->after_rec->FileIndex),
280         jcr->impl->dcr->after_rec->data_len);
281 
282   SendAttrsToDir(jcr, jcr->impl->dcr->after_rec);
283 
284   retval = true;
285 
286 bail_out:
287   if (translated_record) {
288     FreeRecord(jcr->impl->dcr->after_rec);
289     jcr->impl->dcr->after_rec = NULL;
290   }
291 
292   return retval;
293 }
294 
295 /**
296  * Called here for each record from ReadRecords()
297  * This function is used when we do a external clone of a Job e.g.
298  * this SD is the reading SD. And a remote SD is the writing SD.
299  *
300  * Returns: true if OK
301  *           false if error
302  */
CloneRecordToRemoteSd(DeviceControlRecord * dcr,DeviceRecord * rec)303 static bool CloneRecordToRemoteSd(DeviceControlRecord* dcr, DeviceRecord* rec)
304 {
305   POOLMEM* msgsave;
306   JobControlRecord* jcr = dcr->jcr;
307   char buf1[100], buf2[100];
308   BareosSocket* sd = jcr->store_bsock;
309   bool send_eod, send_header;
310 
311   /*
312    * If label discard it
313    */
314   if (rec->FileIndex < 0) { return true; }
315 
316   /*
317    * See if this is the first record being processed.
318    */
319   if (rec->last_FileIndex == 0) {
320     /*
321      * Initialize the last counters so we can compare
322      * things in the next run through here.
323      */
324     rec->last_VolSessionId = rec->VolSessionId;
325     rec->last_VolSessionTime = rec->VolSessionTime;
326     rec->last_FileIndex = rec->FileIndex;
327     rec->last_Stream = rec->Stream;
328     jcr->JobFiles = 1;
329 
330     /*
331      * Need to send both a new header only.
332      */
333     send_eod = false;
334     send_header = true;
335   } else {
336     /*
337      * See if we are changing file or stream type.
338      */
339     if (rec->VolSessionId != rec->last_VolSessionId ||
340         rec->VolSessionTime != rec->last_VolSessionTime ||
341         rec->FileIndex != rec->last_FileIndex ||
342         rec->Stream != rec->last_Stream) {
343       /*
344        * See if we are changing the FileIndex e.g.
345        * start processing the next file in the backup stream.
346        */
347       if (rec->FileIndex != rec->last_FileIndex) { jcr->JobFiles++; }
348 
349       /*
350        * Keep track of the new state.
351        */
352       rec->last_VolSessionId = rec->VolSessionId;
353       rec->last_VolSessionTime = rec->VolSessionTime;
354       rec->last_FileIndex = rec->FileIndex;
355       rec->last_Stream = rec->Stream;
356 
357       /*
358        * Need to send both a EOD and a new header.
359        */
360       send_eod = true;
361       send_header = true;
362     } else {
363       send_eod = false;
364       send_header = false;
365     }
366   }
367 
368   /*
369    * Send a EOD when needed.
370    */
371   if (send_eod) {
372     if (!sd->signal(BNET_EOD)) { /* indicate end of file data */
373       if (!jcr->IsJobCanceled()) {
374         Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
375               sd->bstrerror());
376       }
377       return false;
378     }
379   }
380 
381   /*
382    * Send a header when needed.
383    */
384   if (send_header) {
385     if (!sd->fsend("%ld %d 0", rec->FileIndex, rec->Stream)) {
386       if (!jcr->IsJobCanceled()) {
387         Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
388               sd->bstrerror());
389       }
390       return false;
391     }
392   }
393 
394   /*
395    * Send the record data.
396    * We don't want to copy the data from the record to the socket structure
397    * so we save the original msg pointer and use the record data pointer for
398    * sending and restore the original msg pointer when done.
399    */
400   msgsave = sd->msg;
401   sd->msg = rec->data;
402   sd->message_length = rec->data_len;
403 
404   if (!sd->send()) {
405     sd->msg = msgsave;
406     sd->message_length = 0;
407     if (!jcr->IsJobCanceled()) {
408       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
409             sd->bstrerror());
410     }
411     return false;
412   }
413 
414   jcr->JobBytes += sd->message_length;
415   sd->msg = msgsave;
416 
417   Dmsg5(200, "wrote_record JobId=%d FI=%s SessId=%d Strm=%s len=%d\n",
418         jcr->JobId, FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
419         stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
420 
421   return true;
422 }
423 
424 /**
425  * Check autoinflation/autodeflation settings.
426  */
CheckAutoXflation(JobControlRecord * jcr)427 static inline void CheckAutoXflation(JobControlRecord* jcr)
428 {
429   /*
430    * See if the autoxflateonreplication flag is set to true then we allow
431    * autodeflation and autoinflation to take place.
432    */
433   if (me->autoxflateonreplication) { return; }
434 
435   /*
436    * Check autodeflation.
437    */
438   switch (jcr->impl->read_dcr->autodeflate) {
439     case IO_DIRECTION_IN:
440     case IO_DIRECTION_INOUT:
441       Dmsg0(200, "Clearing autodeflate on read_dcr\n");
442       jcr->impl->read_dcr->autodeflate = IO_DIRECTION_NONE;
443       break;
444     default:
445       break;
446   }
447 
448   if (jcr->impl->dcr) {
449     switch (jcr->impl->dcr->autodeflate) {
450       case IO_DIRECTION_OUT:
451       case IO_DIRECTION_INOUT:
452         Dmsg0(200, "Clearing autodeflate on write dcr\n");
453         jcr->impl->dcr->autodeflate = IO_DIRECTION_NONE;
454         break;
455       default:
456         break;
457     }
458   }
459 
460   /*
461    * Check autoinflation.
462    */
463   switch (jcr->impl->read_dcr->autoinflate) {
464     case IO_DIRECTION_IN:
465     case IO_DIRECTION_INOUT:
466       Dmsg0(200, "Clearing autoinflate on read_dcr\n");
467       jcr->impl->read_dcr->autoinflate = IO_DIRECTION_NONE;
468       break;
469     default:
470       break;
471   }
472 
473   if (jcr->impl->dcr) {
474     switch (jcr->impl->dcr->autoinflate) {
475       case IO_DIRECTION_OUT:
476       case IO_DIRECTION_INOUT:
477         Dmsg0(200, "Clearing autoinflate on write dcr\n");
478         jcr->impl->dcr->autoinflate = IO_DIRECTION_NONE;
479         break;
480       default:
481         break;
482     }
483   }
484 }
485 
486 /**
487  * Read Data and commit to new job.
488  */
DoMacRun(JobControlRecord * jcr)489 bool DoMacRun(JobControlRecord* jcr)
490 {
491   utime_t now;
492   char ec1[50];
493   const char* Type;
494   bool ok = true;
495   bool acquire_fail = false;
496   BareosSocket* dir = jcr->dir_bsock;
497   Device* dev = jcr->impl->dcr->dev;
498 
499   switch (jcr->getJobType()) {
500     case JT_MIGRATE:
501       Type = "Migration";
502       break;
503     case JT_ARCHIVE:
504       Type = "Archive";
505       break;
506     case JT_COPY:
507       Type = "Copy";
508       break;
509     case JT_BACKUP:
510       Type = "Virtual Backup";
511       break;
512     default:
513       Type = "Unknown";
514       break;
515   }
516 
517   Dmsg0(20, "Start read data.\n");
518 
519   if (jcr->impl->NumReadVolumes == 0) {
520     Jmsg(jcr, M_FATAL, 0, _("No Volume names found for %s.\n"), Type);
521     goto bail_out;
522   }
523 
524   /*
525    * Check autoinflation/autodeflation settings.
526    */
527   CheckAutoXflation(jcr);
528 
529   /*
530    * See if we perform both read and write or read only.
531    */
532   if (jcr->impl->remote_replicate) {
533     BareosSocket* sd;
534 
535     if (!jcr->impl->read_dcr) {
536       Jmsg(jcr, M_FATAL, 0, _("Read device not properly initialized.\n"));
537       goto bail_out;
538     }
539 
540     Dmsg1(100, "read_dcr=%p\n", jcr->impl->read_dcr);
541     Dmsg3(200, "Found %d volumes names for %s. First=%s\n",
542           jcr->impl->NumReadVolumes, Type, jcr->impl->VolList->VolumeName);
543 
544     /*
545      * Ready devices for reading.
546      */
547     if (!AcquireDeviceForRead(jcr->impl->read_dcr)) {
548       ok = false;
549       acquire_fail = true;
550       goto bail_out;
551     }
552 
553     Dmsg2(200, "===== After acquire pos %u:%u\n",
554           jcr->impl->read_dcr->dev->file, jcr->impl->read_dcr->dev->block_num);
555 
556     jcr->sendJobStatus(JS_Running);
557 
558     /*
559      * Set network buffering.
560      */
561     sd = jcr->store_bsock;
562     if (!sd->SetBufferSize(me->max_network_buffer_size, BNET_SETBUF_WRITE)) {
563       Jmsg(jcr, M_FATAL, 0, _("Cannot set buffer size SD->SD.\n"));
564       ok = false;
565       goto bail_out;
566     }
567 
568     /*
569      * Let the remote SD know we are about to start the replication.
570      */
571     sd->fsend(start_replicate);
572     Dmsg1(110, ">stored: %s", sd->msg);
573 
574     /*
575      * Expect to receive back the Ticket number.
576      */
577     if (BgetMsg(sd) >= 0) {
578       Dmsg1(110, "<stored: %s", sd->msg);
579       if (sscanf(sd->msg, OK_start_replicate, &jcr->impl->Ticket) != 1) {
580         Jmsg(jcr, M_FATAL, 0, _("Bad response to start replicate: %s\n"),
581              sd->msg);
582         goto bail_out;
583       }
584       Dmsg1(110, "Got Ticket=%d\n", jcr->impl->Ticket);
585     } else {
586       Jmsg(jcr, M_FATAL, 0,
587            _("Bad response from stored to start replicate command\n"));
588       goto bail_out;
589     }
590 
591     /*
592      * Let the remote SD know we are now really going to send the data.
593      */
594     sd->fsend(ReplicateData, jcr->impl->Ticket);
595     Dmsg1(110, ">stored: %s", sd->msg);
596 
597     /*
598      * Expect to get response to the replicate data cmd from Storage daemon
599      */
600     if (!response(jcr, sd, OK_data, "replicate data")) {
601       ok = false;
602       goto bail_out;
603     }
604 
605     /*
606      * Update the initial Job Statistics.
607      */
608     now = (utime_t)time(NULL);
609     UpdateJobStatistics(jcr, now);
610 
611     /*
612      * Read all data and send it to remote SD.
613      */
614     ok = ReadRecords(jcr->impl->read_dcr, CloneRecordToRemoteSd,
615                      MountNextReadVolume);
616 
617     /*
618      * Send the last EOD to close the last data transfer and a next EOD to
619      * signal the remote we are done.
620      */
621     if (!sd->signal(BNET_EOD) || !sd->signal(BNET_EOD)) {
622       if (!jcr->IsJobCanceled()) {
623         Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
624               sd->bstrerror());
625       }
626       goto bail_out;
627     }
628 
629     /*
630      * Expect to get response that the replicate data succeeded.
631      */
632     if (!response(jcr, sd, OK_replicate, "replicate data")) {
633       ok = false;
634       goto bail_out;
635     }
636 
637     /*
638      * End replicate session.
639      */
640     sd->fsend(end_replicate);
641     Dmsg1(110, ">stored: %s", sd->msg);
642 
643     /*
644      * Expect to get response to the end replicate cmd from Storage daemon
645      */
646     if (!response(jcr, sd, OK_end_replicate, "end replicate")) {
647       ok = false;
648       goto bail_out;
649     }
650 
651     /* Inform Storage daemon that we are done */
652     sd->signal(BNET_TERMINATE);
653   } else {
654     if (!jcr->impl->read_dcr) {
655       Jmsg(jcr, M_FATAL, 0, _("Read device not properly initialized.\n"));
656       goto bail_out;
657     }
658 
659     Dmsg2(100, "read_dcr=%p write_dcr=%p\n", jcr->impl->read_dcr,
660           jcr->impl->dcr);
661     Dmsg3(200, "Found %d volumes names for %s. First=%s\n",
662           jcr->impl->NumReadVolumes, Type, jcr->impl->VolList->VolumeName);
663 
664     /*
665      * Ready devices for reading and writing.
666      */
667     if (!AcquireDeviceForAppend(jcr->impl->dcr) ||
668         !AcquireDeviceForRead(jcr->impl->read_dcr)) {
669       ok = false;
670       acquire_fail = true;
671       goto bail_out;
672     }
673 
674     Dmsg2(200, "===== After acquire pos %u:%u\n", jcr->impl->dcr->dev->file,
675           jcr->impl->dcr->dev->block_num);
676 
677     jcr->sendJobStatus(JS_Running);
678 
679     /*
680      * Update the initial Job Statistics.
681      */
682     now = (utime_t)time(NULL);
683     UpdateJobStatistics(jcr, now);
684 
685     if (!BeginDataSpool(jcr->impl->dcr)) {
686       ok = false;
687       goto bail_out;
688     }
689 
690     if (!BeginAttributeSpool(jcr)) {
691       ok = false;
692       goto bail_out;
693     }
694 
695     jcr->impl->dcr->VolFirstIndex = jcr->impl->dcr->VolLastIndex = 0;
696     jcr->run_time = time(NULL);
697     SetStartVolPosition(jcr->impl->dcr);
698     jcr->JobFiles = 0;
699 
700     /*
701      * Read all data and make a local clone of it.
702      */
703     ok = ReadRecords(jcr->impl->read_dcr, CloneRecordInternally,
704                      MountNextReadVolume);
705   }
706 
707 bail_out:
708   if (!ok) { jcr->setJobStatus(JS_ErrorTerminated); }
709 
710   if (!acquire_fail && !jcr->impl->remote_replicate && jcr->impl->dcr) {
711     /*
712      * Don't use time_t for job_elapsed as time_t can be 32 or 64 bits,
713      *   and the subsequent Jmsg() editing will break
714      */
715     int32_t job_elapsed;
716 
717     Dmsg1(100, "ok=%d\n", ok);
718 
719     if (ok || dev->CanWrite()) {
720       /*
721          memorize current JobStatus and set to
722          JS_Terminated to write into EOS_LABEL
723        */
724       char currentJobStatus = jcr->JobStatus;
725       jcr->setJobStatus(JS_Terminated);
726 
727       /*
728        * Write End Of Session Label
729        */
730       DeviceControlRecord* dcr = jcr->impl->dcr;
731       if (!WriteSessionLabel(dcr, EOS_LABEL)) {
732         /*
733          * Print only if ok and not cancelled to avoid spurious messages
734          */
735 
736         if (ok && !jcr->IsJobCanceled()) {
737           Jmsg1(jcr, M_FATAL, 0, _("Error writing end session label. ERR=%s\n"),
738                 dev->bstrerror());
739         }
740         jcr->setJobStatus(JS_ErrorTerminated);
741         ok = false;
742       } else {
743         /* restore JobStatus */
744         jcr->setJobStatus(currentJobStatus);
745       }
746       /*
747        * Flush out final partial block of this session
748        */
749       if (!jcr->impl->dcr->WriteBlockToDevice()) {
750         Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
751               dev->print_name(), dev->bstrerror());
752         Dmsg0(100, _("Set ok=FALSE after WriteBlockToDevice.\n"));
753         ok = false;
754       }
755       Dmsg2(200, "Flush block to device pos %u:%u\n", dev->file,
756             dev->block_num);
757     }
758 
759 
760     if (!ok) {
761       DiscardDataSpool(jcr->impl->dcr);
762     } else {
763       /*
764        * Note: if commit is OK, the device will remain blocked
765        */
766       CommitDataSpool(jcr->impl->dcr);
767     }
768 
769     job_elapsed = time(NULL) - jcr->run_time;
770     if (job_elapsed <= 0) { job_elapsed = 1; }
771 
772     Jmsg(jcr, M_INFO, 0,
773          _("Elapsed time=%02d:%02d:%02d, Transfer rate=%s Bytes/second\n"),
774          job_elapsed / 3600, job_elapsed % 3600 / 60, job_elapsed % 60,
775          edit_uint64_with_suffix(jcr->JobBytes / job_elapsed, ec1));
776 
777     /*
778      * Release the device -- and send final Vol info to DIR
779      */
780     ReleaseDevice(jcr->impl->dcr);
781 
782     if (!ok || JobCanceled(jcr)) {
783       DiscardAttributeSpool(jcr);
784     } else {
785       CommitAttributeSpool(jcr);
786     }
787   }
788 
789   if (jcr->impl->read_dcr) {
790     if (!ReleaseDevice(jcr->impl->read_dcr)) { ok = false; }
791   }
792 
793   jcr->sendJobStatus(); /* update director */
794 
795   Dmsg0(30, "Done reading.\n");
796   jcr->end_time = time(NULL);
797   DequeueMessages(jcr); /* send any queued messages */
798   if (ok) { jcr->setJobStatus(JS_Terminated); }
799 
800   GeneratePluginEvent(jcr, bsdEventJobEnd);
801   dir->fsend(Job_end, jcr->Job, jcr->JobStatus, jcr->JobFiles,
802              edit_uint64(jcr->JobBytes, ec1), jcr->JobErrors);
803   Dmsg4(100, Job_end, jcr->Job, jcr->JobStatus, jcr->JobFiles, ec1);
804 
805   dir->signal(BNET_EOD); /* send EOD to Director daemon */
806   FreePlugins(jcr);      /* release instantiated plugins */
807 
808   return false; /* Continue DIR session ? */
809 }
810 
811 } /* namespace storagedaemon */
812