2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * SD -- vbackup.c -- responsible for doing virtual backup jobs.
22 * Kern Sibbald, January MMVI
29 /* Import functions */
30 extern char Job_end[];
32 /* Forward referenced subroutines */
33 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
36 * Read Data and send to File Daemon
37 * Returns: false on failure
40 bool do_vbackup(JCR *jcr)
43 BSOCK *dir = jcr->dir_bsock;
48 switch(jcr->getJobType()) {
59 Type = "Virtual Backup";
67 Dmsg0(20, "Start read data.\n");
69 if (!jcr->read_dcr || !jcr->dcr) {
70 Jmsg(jcr, M_FATAL, 0, _("Read and write devices not properly initialized.\n"));
73 Dmsg2(100, "read_dcr=%p write_dcr=%p\n", jcr->read_dcr, jcr->dcr);
75 if (jcr->NumReadVolumes == 0) {
76 Jmsg(jcr, M_FATAL, 0, _("No Volume names found for %s.\n"), Type);
80 Dmsg3(200, "Found %d volumes names for %s. First=%s\n", jcr->NumReadVolumes,
81 jcr->VolList->VolumeName, Type);
83 ASSERT(jcr->read_dcr != jcr->dcr);
84 ASSERT(jcr->read_dcr->dev != jcr->dcr->dev);
85 /* Ready devices for reading and writing */
86 if (!acquire_device_for_read(jcr->read_dcr) ||
87 !acquire_device_for_append(jcr->dcr)) {
88 jcr->setJobStatus(JS_ErrorTerminated);
92 Dmsg2(200, "===== After acquire pos %u:%u\n", jcr->dcr->dev->file, jcr->dcr->dev->block_num);
93 jcr->sendJobStatus(JS_Running);
95 begin_data_spool(jcr->dcr);
96 begin_attribute_spool(jcr);
98 jcr->dcr->VolFirstIndex = jcr->dcr->VolLastIndex = 0;
99 jcr->run_time = time(NULL);
100 set_start_vol_position(jcr->dcr);
103 jcr->dcr->set_ameta();
104 jcr->read_dcr->set_ameta();
105 ok = read_records(jcr->read_dcr, record_cb, mount_next_read_volume);
113 jcr->dcr->set_ameta();
115 Dmsg1(100, "ok=%d\n", ok);
116 if (ok || dev->can_write()) {
117 /* Flush out final ameta partial block of this session */
118 if (!jcr->dcr->write_final_block_to_device()) {
119 Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
120 dev->print_name(), dev->bstrerror());
121 Dmsg0(100, _("Set ok=FALSE after write_final_block_to_device.\n"));
124 Dmsg2(200, "Flush block to device pos %u:%u\n", dev->file, dev->block_num);
126 flush_jobmedia_queue(jcr);
128 discard_data_spool(jcr->dcr);
130 /* Note: if commit is OK, the device will remain blocked */
131 commit_data_spool(jcr->dcr);
135 * Don't use time_t for job_elapsed as time_t can be 32 or 64 bits,
136 * and the subsequent Jmsg() editing will break
138 int32_t job_elapsed = time(NULL) - jcr->run_time;
140 if (job_elapsed <= 0) {
144 Jmsg(jcr, M_INFO, 0, _("Elapsed time=%02d:%02d:%02d, Transfer rate=%s Bytes/second\n"),
145 job_elapsed / 3600, job_elapsed % 3600 / 60, job_elapsed % 60,
146 edit_uint64_with_suffix(jcr->JobBytes / job_elapsed, ec1));
148 /* Release the device -- and send final Vol info to DIR */
149 release_device(jcr->dcr);
151 if (!ok || job_canceled(jcr)) {
152 discard_attribute_spool(jcr);
154 commit_attribute_spool(jcr);
159 if (!release_device(jcr->read_dcr)) {
164 jcr->sendJobStatus(); /* update director */
166 Dmsg0(30, "Done reading.\n");
167 jcr->end_time = time(NULL);
168 dequeue_messages(jcr); /* send any queued messages */
170 jcr->setJobStatus(JS_Terminated);
172 generate_daemon_event(jcr, "JobEnd");
173 generate_plugin_event(jcr, bsdEventJobEnd);
174 dir->fsend(Job_end, jcr->Job, jcr->JobStatus, jcr->JobFiles,
175 edit_uint64(jcr->JobBytes, ec1), jcr->JobErrors);
176 Dmsg4(100, Job_end, jcr->Job, jcr->JobStatus, jcr->JobFiles, ec1);
178 dir->signal(BNET_EOD); /* send EOD to Director daemon */
179 free_plugins(jcr); /* release instantiated plugins */
186 * Called here for each record from read_records()
187 * Returns: true if OK
190 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
193 DEVICE *dev = jcr->dcr->dev;
194 char buf1[100], buf2[100];
195 bool restoredatap = false;
196 POOLMEM *orgdata = NULL;
197 uint32_t orgdata_len = 0;
200 /* If label and not for us, discard it */
201 if (rec->FileIndex < 0 && rec->match_stat <= 0) {
205 /* We want to write SOS_LABEL and EOS_LABEL discard all others */
206 switch (rec->FileIndex) {
211 ret = true; /* don't write vol labels */
216 * For normal migration jobs, FileIndex values are sequential because
217 * we are dealing with one job. However, for Vbackup (consolidation),
218 * we will be getting records from multiple jobs and writing them back
219 * out, so we need to ensure that the output FileIndex is sequential.
220 * We do so by detecting a FileIndex change and incrementing the
221 * JobFiles, which we then use as the output FileIndex.
223 if (rec->FileIndex >= 0) {
224 /* If something changed, increment FileIndex */
225 if (rec->VolSessionId != rec->last_VolSessionId ||
226 rec->VolSessionTime != rec->last_VolSessionTime ||
227 rec->FileIndex != rec->last_FileIndex) {
229 rec->last_VolSessionId = rec->VolSessionId;
230 rec->last_VolSessionTime = rec->VolSessionTime;
231 rec->last_FileIndex = rec->FileIndex;
233 rec->FileIndex = jcr->JobFiles; /* set sequential output FileIndex */
237 * Modify record SessionId and SessionTime to correspond to
240 rec->VolSessionId = jcr->VolSessionId;
241 rec->VolSessionTime = jcr->VolSessionTime;
242 Dmsg5(200, "before write JobId=%d FI=%s SessId=%d Strm=%s len=%d\n",
244 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
245 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
247 if (!jcr->dcr->write_record(rec)) {
248 Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
249 dev->print_name(), dev->bstrerror());
253 rec->VolSessionId = rec->last_VolSessionId;
254 rec->VolSessionTime = rec->last_VolSessionTime;
255 if (rec->FileIndex < 0) {
256 ret = true; /* don't send LABELs to Dir */
259 jcr->JobBytes += rec->data_len; /* increment bytes this job */
260 Dmsg5(500, "wrote_record JobId=%d FI=%s SessId=%d Strm=%s len=%d\n",
262 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
263 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
265 send_attrs_to_dir(jcr, rec);
271 rec->data_len = orgdata_len;