]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/vbackup.c
Change copyright as per agreement with FSFE
[bacula/bacula] / bacula / src / stored / vbackup.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2016 Kern Sibbald
5
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.
8
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.
13
14    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  * SD -- vbackup.c --  responsible for doing virtual backup jobs.
21  *
22  *     Kern Sibbald, January MMVI
23  *
24  */
25
26 #include "bacula.h"
27 #include "stored.h"
28
29 /* Import functions */
30 extern char Job_end[];
31
32 /* Forward referenced subroutines */
33 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
34
35 /*
36  *  Read Data and send to File Daemon
37  *   Returns: false on failure
38  *            true  on success
39  */
40 bool do_vbackup(JCR *jcr)
41 {
42    bool ok = true;
43    BSOCK *dir = jcr->dir_bsock;
44    const char *Type;
45    char ec1[50];
46    DEVICE *dev;
47
48    switch(jcr->getJobType()) {
49    case JT_MIGRATE:
50       Type = "Migration";
51       break;
52    case JT_ARCHIVE:
53       Type = "Archive";
54       break;
55    case JT_COPY:
56       Type = "Copy";
57       break;
58    case JT_BACKUP:
59       Type = "Virtual Backup";
60       break;
61    default:
62       Type = "Unknown";
63       break;
64    }
65
66
67    Dmsg0(20, "Start read data.\n");
68
69    if (!jcr->read_dcr || !jcr->dcr) {
70       Jmsg(jcr, M_FATAL, 0, _("Read and write devices not properly initialized.\n"));
71       goto bail_out;
72    }
73    Dmsg2(100, "read_dcr=%p write_dcr=%p\n", jcr->read_dcr, jcr->dcr);
74
75    if (jcr->NumReadVolumes == 0) {
76       Jmsg(jcr, M_FATAL, 0, _("No Volume names found for %s.\n"), Type);
77       goto bail_out;
78    }
79
80    Dmsg3(200, "Found %d volumes names for %s. First=%s\n", jcr->NumReadVolumes,
81       jcr->VolList->VolumeName, Type);
82
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);
89       goto bail_out;
90    }
91
92    Dmsg2(200, "===== After acquire pos %u:%u\n", jcr->dcr->dev->file, jcr->dcr->dev->block_num);
93    jcr->sendJobStatus(JS_Running);
94
95    begin_data_spool(jcr->dcr);
96    begin_attribute_spool(jcr);
97
98    jcr->dcr->VolFirstIndex = jcr->dcr->VolLastIndex = 0;
99    jcr->run_time = time(NULL);
100    set_start_vol_position(jcr->dcr);
101
102    jcr->JobFiles = 0;
103    jcr->dcr->set_ameta();
104    jcr->read_dcr->set_ameta();
105    ok = read_records(jcr->read_dcr, record_cb, mount_next_read_volume);
106    goto ok_out;
107
108 bail_out:
109    ok = false;
110
111 ok_out:
112    if (jcr->dcr) {
113       jcr->dcr->set_ameta();
114       dev = jcr->dcr->dev;
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"));
122             ok = false;
123          }
124          Dmsg2(200, "Flush block to device pos %u:%u\n", dev->file, dev->block_num);
125       }
126       flush_jobmedia_queue(jcr);
127       if (!ok) {
128          discard_data_spool(jcr->dcr);
129       } else {
130          /* Note: if commit is OK, the device will remain blocked */
131          commit_data_spool(jcr->dcr);
132       }
133
134       /*
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
137        */
138       int32_t job_elapsed = time(NULL) - jcr->run_time;
139
140       if (job_elapsed <= 0) {
141          job_elapsed = 1;
142       }
143
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));
147
148       /* Release the device -- and send final Vol info to DIR */
149       release_device(jcr->dcr);
150
151       if (!ok || job_canceled(jcr)) {
152          discard_attribute_spool(jcr);
153       } else {
154          commit_attribute_spool(jcr);
155       }
156    }
157
158    if (jcr->read_dcr) {
159       if (!release_device(jcr->read_dcr)) {
160          ok = false;
161       }
162    }
163
164    jcr->sendJobStatus();              /* update director */
165
166    Dmsg0(30, "Done reading.\n");
167    jcr->end_time = time(NULL);
168    dequeue_messages(jcr);             /* send any queued messages */
169    if (ok) {
170       jcr->setJobStatus(JS_Terminated);
171    }
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);
177
178    dir->signal(BNET_EOD);             /* send EOD to Director daemon */
179    free_plugins(jcr);                 /* release instantiated plugins */
180
181    return ok;
182 }
183
184
185 /*
186  * Called here for each record from read_records()
187  *  Returns: true if OK
188  *           false if error
189  */
190 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
191 {
192    JCR *jcr = dcr->jcr;
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;
198    bool ret = false;
199
200    /* If label and not for us, discard it */
201    if (rec->FileIndex < 0 && rec->match_stat <= 0) {
202       ret = true;
203       goto bail_out;
204    }
205    /* We want to write SOS_LABEL and EOS_LABEL discard all others */
206    switch (rec->FileIndex) {
207    case PRE_LABEL:
208    case VOL_LABEL:
209    case EOT_LABEL:
210    case EOM_LABEL:
211       ret = true;                    /* don't write vol labels */
212       goto bail_out;
213    }
214
215    /*
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.
222     */
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) {
228          jcr->JobFiles++;
229          rec->last_VolSessionId = rec->VolSessionId;
230          rec->last_VolSessionTime = rec->VolSessionTime;
231          rec->last_FileIndex = rec->FileIndex;
232       }
233       rec->FileIndex = jcr->JobFiles;     /* set sequential output FileIndex */
234    }
235
236    /*
237     * Modify record SessionId and SessionTime to correspond to
238     * output.
239     */
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",
243       jcr->JobId,
244       FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
245       stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
246
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());
250       goto bail_out;
251    }
252    /* Restore packet */
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 */
257       goto bail_out;
258    }
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",
261       jcr->JobId,
262       FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
263       stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
264
265    send_attrs_to_dir(jcr, rec);
266    ret = true;
267
268 bail_out:
269    if (restoredatap) {
270       rec->data = orgdata;
271       rec->data_len = orgdata_len;
272    }
273    return ret;
274 }