1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 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 * Subroutines to handle Catalog reqests sent to the Director
21 * Reqests/commands from the Director are handled in dircmd.c
22 *
23 * Kern Sibbald, December 2000
24 */
25
26 #include "bacula.h" /* pull in global headers */
27 #include "stored.h" /* pull in Storage Deamon headers */
28
29 static const int dbglvl = 200;
30
31 /* Requests sent to the Director */
32 static char Find_media[] = "CatReq JobId=%ld FindMedia=%d pool_name=%s media_type=%s vol_type=%d\n";
33 static char Get_Vol_Info[] = "CatReq JobId=%ld GetVolInfo VolName=%s write=%d\n";
34 static char Update_media[] = "CatReq JobId=%ld UpdateMedia VolName=%s"
35 " VolJobs=%u VolFiles=%u VolBlocks=%u VolBytes=%s VolABytes=%s"
36 " VolHoleBytes=%s VolHoles=%u VolMounts=%u"
37 " VolErrors=%u VolWrites=%u MaxVolBytes=%s EndTime=%s VolStatus=%s"
38 " Slot=%d relabel=%d InChanger=%d VolReadTime=%s VolWriteTime=%s"
39 " VolFirstWritten=%s VolType=%u VolParts=%d VolCloudParts=%d"
40 " LastPartBytes=%lld Enabled=%d Recycle=%d\n";
41 static char Create_jobmedia[] = "CatReq JobId=%ld CreateJobMedia\n";
42 static char FileAttributes[] = "UpdCat JobId=%ld FileAttributes ";
43
44 /* Responses received from the Director */
45 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%lu"
46 " VolBlocks=%lu VolBytes=%lld VolABytes=%lld"
47 " VolHoleBytes=%lld VolHoles=%lu VolMounts=%lu"
48 " VolErrors=%lu VolWrites=%lu"
49 " MaxVolBytes=%lld VolCapacityBytes=%lld VolStatus=%20s"
50 " Slot=%ld MaxVolJobs=%lu MaxVolFiles=%lu InChanger=%ld"
51 " VolReadTime=%lld VolWriteTime=%lld EndFile=%lu EndBlock=%lu"
52 " VolType=%lu LabelType=%ld MediaId=%lld ScratchPoolId=%lld"
53 " VolParts=%d VolCloudParts=%d LastPartBytes=%lld Enabled=%d"
54 " Recycle=%d\n";
55
56
57 static char OK_create[] = "1000 OK CreateJobMedia\n";
58
59 static bthread_mutex_t vol_info_mutex = BTHREAD_MUTEX_PRIORITY(PRIO_SD_VOL_INFO);
60
61 #ifdef needed
62
63 Note: if you turn this on, be sure to add the Recycle Flag
64
65 static char Device_update[] = "DevUpd JobId=%ld device=%s "
66 "append=%d read=%d num_writers=%d "
67 "open=%d labeled=%d offline=%d "
68 "reserved=%d max_writers=%d "
69 "autoselect=%d autochanger=%d "
70 "enabled=%d "
71 "changer_name=%s media_type=%s volume_name=%s\n";
72
73
74 /** Send update information about a device to Director */
dir_update_device(JCR * jcr,DEVICE * dev)75 bool dir_update_device(JCR *jcr, DEVICE *dev)
76 {
77 BSOCK *dir = jcr->dir_bsock;
78 POOL_MEM dev_name, VolumeName, MediaType, ChangerName;
79 DEVRES *device = dev->device;
80 bool ok;
81
82 pm_strcpy(dev_name, device->hdr.name);
83 bash_spaces(dev_name);
84 if (dev->is_labeled()) {
85 pm_strcpy(VolumeName, dev->VolHdr.VolumeName);
86 } else {
87 pm_strcpy(VolumeName, "*");
88 }
89 bash_spaces(VolumeName);
90 pm_strcpy(MediaType, device->media_type);
91 bash_spaces(MediaType);
92 if (device->changer_res) {
93 pm_strcpy(ChangerName, device->changer_res->hdr.name);
94 bash_spaces(ChangerName);
95 } else {
96 pm_strcpy(ChangerName, "*");
97 }
98 ok = dir->fsend(Device_update,
99 jcr->JobId,
100 dev_name.c_str(),
101 dev->can_append()!=0,
102 dev->can_read()!=0, dev->num_writers,
103 dev->is_open()!=0, dev->is_labeled()!=0,
104 dev->is_offline()!=0, dev->reserved_device,
105 dev->is_tape()?100000:1,
106 dev->autoselect, 0,
107 dev->enabled,
108 ChangerName.c_str(), MediaType.c_str(), VolumeName.c_str());
109 Dmsg1(dbglvl, ">dird: %s\n", dir->msg);
110 return ok;
111 }
112
dir_update_changer(JCR * jcr,AUTOCHANGER * changer)113 bool dir_update_changer(JCR *jcr, AUTOCHANGER *changer)
114 {
115 BSOCK *dir = jcr->dir_bsock;
116 POOL_MEM dev_name, MediaType;
117 DEVRES *device;
118 bool ok;
119
120 pm_strcpy(dev_name, changer->hdr.name);
121 bash_spaces(dev_name);
122 device = (DEVRES *)changer->device->first();
123 pm_strcpy(MediaType, device->media_type);
124 bash_spaces(MediaType);
125 /* This is mostly to indicate that we are here */
126 ok = dir->fsend(Device_update,
127 jcr->JobId,
128 dev_name.c_str(), /* Changer name */
129 0, 0, 0, /* append, read, num_writers */
130 0, 0, 0, /* is_open, is_labeled, offline */
131 0, 0, /* reserved, max_writers */
132 0, /* Autoselect */
133 0, /* Enabled */
134 changer->device->size(), /* Number of devices */
135 "0", /* PoolId */
136 "*", /* ChangerName */
137 MediaType.c_str(), /* MediaType */
138 "*"); /* VolName */
139 Dmsg1(dbglvl, ">dird: %s\n", dir->msg);
140 return ok;
141 }
142 #endif
143
144
145 static AskDirHandler *askdir_handler = NULL; /* must be true when inside a "btools" */
146
147 /*
148 * btools must call this function, to modify behavior of some functions here
149 */
init_askdir_handler(AskDirHandler * new_askdir_handler)150 AskDirHandler *init_askdir_handler(AskDirHandler *new_askdir_handler)
151 {
152 AskDirHandler *old = askdir_handler;
153 askdir_handler = new_askdir_handler;
154 return old;
155 }
156
157 /*
158 * Alternate function used by btools
159 */
dir_ask_sysop_to_mount_volume(DCR * dcr,bool)160 bool AskDirHandler::dir_ask_sysop_to_mount_volume(DCR *dcr, bool /*writing*/)
161 {
162 DEVICE *dev = dcr->dev;
163 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
164 dcr->VolumeName, dev->print_name());
165 dev->close(dcr);
166 getchar();
167 return true;
168 }
169
dir_get_volume_info(DCR * dcr,const char * VolumeName,enum get_vol_info_rw writing)170 bool AskDirHandler::dir_get_volume_info(DCR *dcr, const char *VolumeName, enum get_vol_info_rw writing)
171 {
172 Dmsg0(100, "Fake dir_get_volume_info\n");
173 dcr->setVolCatName(VolumeName);
174 Dmsg2(500, "Vol=%s VolType=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatType);
175 return 1;
176 }
177
178 /**
179 * Send current JobStatus to Director
180 */
dir_send_job_status(JCR * jcr)181 bool dir_send_job_status(JCR *jcr)
182 {
183 if (askdir_handler) {
184 return askdir_handler->dir_send_job_status(jcr);
185 }
186
187 return jcr->sendJobStatus();
188 }
189
190 /**
191 * Common routine for:
192 * dir_get_volume_info()
193 * and
194 * dir_find_next_appendable_volume()
195 *
196 * NOTE!!! All calls to this routine must be protected by
197 * locking vol_info_mutex before calling it so that
198 * we don't have one thread modifying the parameters
199 * and another reading them.
200 *
201 * Returns: true on success and vol info in dcr->VolCatInfo
202 * false on failure
203 */
do_get_volume_info(DCR * dcr)204 static bool do_get_volume_info(DCR *dcr)
205 {
206 JCR *jcr = dcr->jcr;
207 BSOCK *dir = jcr->dir_bsock;
208 VOLUME_CAT_INFO vol;
209 int n;
210 int32_t Enabled, Recycle;
211 int32_t InChanger;
212
213 dcr->setVolCatInfo(false);
214 if (dir->recv() <= 0) {
215 Dmsg0(dbglvl, "getvolname error bnet_recv\n");
216 Mmsg(jcr->errmsg, _("Network error on bnet_recv in req_vol_info.\n"));
217 return false;
218 }
219 memset(&vol, 0, sizeof(vol));
220 n = sscanf(dir->msg, OK_media, vol.VolCatName,
221 &vol.VolCatJobs, &vol.VolCatFiles,
222 &vol.VolCatBlocks, &vol.VolCatAmetaBytes,
223 &vol.VolCatAdataBytes, &vol.VolCatHoleBytes,
224 &vol.VolCatHoles, &vol.VolCatMounts, &vol.VolCatErrors,
225 &vol.VolCatWrites, &vol.VolCatMaxBytes,
226 &vol.VolCatCapacityBytes, vol.VolCatStatus,
227 &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
228 &InChanger, &vol.VolReadTime, &vol.VolWriteTime,
229 &vol.EndFile, &vol.EndBlock, &vol.VolCatType,
230 &vol.LabelType, &vol.VolMediaId, &vol.VolScratchPoolId,
231 &vol.VolCatParts, &vol.VolCatCloudParts,
232 &vol.VolLastPartBytes, &Enabled, &Recycle);
233 Dmsg2(dbglvl, "<dird n=%d %s", n, dir->msg);
234 if (n != 31) {
235 Dmsg1(dbglvl, "get_volume_info failed: ERR=%s", dir->msg);
236 /*
237 * Note, we can get an error here either because there is
238 * a comm problem, or if the volume is not a suitable
239 * volume to use, so do not issue a Jmsg() here, do it
240 * in the calling routine.
241 */
242 Mmsg(jcr->errmsg, _("Error getting Volume info: %s"), dir->msg);
243 return false;
244 }
245 vol.InChanger = InChanger; /* bool in structure */
246 vol.VolEnabled = Enabled; /* bool in structure */
247 vol.VolRecycle = Recycle; /* bool in structure */
248 vol.is_valid = true;
249 vol.VolCatBytes = vol.VolCatAmetaBytes + vol.VolCatAdataBytes;
250 unbash_spaces(vol.VolCatName);
251 bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName));
252 dcr->VolCatInfo = vol; /* structure assignment */
253
254 Dmsg3(dbglvl, "do_reqest_vol_info return true slot=%d Volume=%s MediaId=%lld\n",
255 dcr->VolCatInfo.Slot, dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolMediaId);
256 Dmsg5(dbglvl, "Dir returned VolCatAmetaBytes=%lld VolCatAdataBytes=%lld Status=%s Vol=%s MediaId=%lld\n",
257 dcr->VolCatInfo.VolCatAmetaBytes, dcr->VolCatInfo.VolCatAdataBytes,
258 dcr->VolCatInfo.VolCatStatus, dcr->VolCatInfo.VolCatName,
259 dcr->VolCatInfo.VolMediaId);
260 return true;
261 }
262
263
264 /**
265 * Get Volume info for a specific volume from the Director's Database
266 *
267 * Returns: true on success (Director guarantees that Pool and MediaType
268 * are correct and VolStatus==Append or
269 * VolStatus==Recycle)
270 * false on failure
271 *
272 * Volume information returned in dcr->VolCatInfo
273 */
dir_get_volume_info(DCR * dcr,const char * VolumeName,enum get_vol_info_rw writing)274 bool dir_get_volume_info(DCR *dcr,
275 const char *VolumeName,
276 enum get_vol_info_rw writing)
277 {
278 if (askdir_handler) {
279 return askdir_handler->dir_get_volume_info(dcr, VolumeName, writing);
280 }
281
282 JCR *jcr = dcr->jcr;
283 BSOCK *dir = jcr->dir_bsock;
284
285 P(vol_info_mutex);
286 dcr->setVolCatName(VolumeName);
287 bash_spaces(dcr->getVolCatName());
288 dir->fsend(Get_Vol_Info, jcr->JobId, dcr->getVolCatName(),
289 writing==GET_VOL_INFO_FOR_WRITE?1:0);
290 Dmsg1(dbglvl, ">dird %s", dir->msg);
291 unbash_spaces(dcr->getVolCatName());
292 bool ok = do_get_volume_info(dcr);
293 V(vol_info_mutex);
294 return ok;
295 }
296
297
298
299 /**
300 * Get info on the next appendable volume in the Director's database
301 *
302 * Returns: true on success dcr->VolumeName is volume
303 * reserve_volume() called on Volume name
304 * false on failure dcr->VolumeName[0] == 0
305 * also sets dcr->found_in_use if at least one
306 * in use volume was found.
307 *
308 * Volume information returned in dcr
309 *
310 */
dir_find_next_appendable_volume(DCR * dcr)311 bool dir_find_next_appendable_volume(DCR *dcr)
312 {
313 /* SD tools setup a handler because they have no connection to Dir */
314 if (askdir_handler) {
315 return askdir_handler->dir_find_next_appendable_volume(dcr);
316 }
317
318 JCR *jcr = dcr->jcr;
319 BSOCK *dir = jcr->dir_bsock;
320 bool rtn;
321 char lastVolume[MAX_NAME_LENGTH];
322 int nb_retry;
323
324 /*
325 * Calculate the number of possible drives + 30 for the size of the
326 * Volume list to consider.
327 */
328 nb_retry = ((rblist *)res_head[R_DEVICE-r_first]->res_list)->size() + 30;
329 Dmsg2(dbglvl, "dir_find_next_appendable_volume: reserved=%d Vol=%s\n",
330 dcr->is_reserved(), dcr->VolumeName);
331 Mmsg(jcr->errmsg, "Unknown error\n");
332
333 /*
334 * Try the thirty oldest or most available volumes. Note,
335 * the most available could already be mounted on another
336 * drive, so we continue looking for a not in use Volume.
337 */
338 lock_volumes();
339 P(vol_info_mutex);
340 dcr->clear_found_in_use();
341 lastVolume[0] = 0;
342 for (int vol_index=1; vol_index < nb_retry; vol_index++) {
343 bash_spaces(dcr->media_type);
344 bash_spaces(dcr->pool_name);
345 dir->fsend(Find_media, jcr->JobId, vol_index, dcr->pool_name, dcr->media_type,
346 dcr->dev->dev_type);
347 unbash_spaces(dcr->media_type);
348 unbash_spaces(dcr->pool_name);
349 Dmsg1(dbglvl, ">dird %s", dir->msg);
350 if (do_get_volume_info(dcr)) {
351 /* Give up if we get the same volume name twice */
352 if (lastVolume[0] && strcmp(lastVolume, dcr->VolumeName) == 0) {
353 Mmsg(jcr->errmsg, "Director returned same volume name=%s twice.\n",
354 lastVolume);
355 Dmsg1(dbglvl, "Got same vol = %s\n", lastVolume);
356 break;
357 }
358 /* If VolCatAdataBytes, we have ALIGNED_DEV */
359 if (dcr->VolCatInfo.VolCatType == 0 && dcr->VolCatInfo.VolCatAdataBytes != 0) {
360 dcr->VolCatInfo.VolCatType = B_ALIGNED_DEV;
361 }
362 /*
363 * If we have VolType and we are disk or aligned, the VolType must match
364 */
365 /* ***FIXME*** find better way to handle voltype */
366 if (dcr->VolCatInfo.VolCatType != 0 &&
367 (dcr->dev->dev_type == B_FILE_DEV || dcr->dev->dev_type == B_ALIGNED_DEV ||
368 dcr->dev->dev_type == B_CLOUD_DEV) &&
369 dcr->dev->dev_type != (int)dcr->VolCatInfo.VolCatType) {
370 Dmsg2(000, "Skip vol. Wanted VolType=%d Got=%d\n", dcr->dev->dev_type, dcr->VolCatInfo.VolCatType);
371 continue;
372 }
373 bstrncpy(lastVolume, dcr->VolumeName, sizeof(lastVolume));
374 if (dcr->can_i_write_volume()) {
375 Dmsg1(dbglvl, "Call reserve_volume for write. Vol=%s\n", dcr->VolumeName);
376 if (reserve_volume(dcr, dcr->VolumeName) == NULL) {
377 Dmsg1(dbglvl, "%s", jcr->errmsg);
378 if (dcr->dev->must_wait()) {
379 rtn = false;
380 dcr->VolumeName[0] = 0;
381 goto get_out;
382 }
383 continue;
384 }
385 Dmsg1(dbglvl, "dir_find_next_appendable_volume return true. vol=%s\n",
386 dcr->VolumeName);
387 rtn = true;
388 goto get_out;
389 } else {
390 Mmsg(jcr->errmsg, "Volume %s is in use.\n", dcr->VolumeName);
391 Dmsg1(dbglvl, "Volume %s is in use.\n", dcr->VolumeName);
392 /* If volume is not usable, it is in use by someone else */
393 dcr->set_found_in_use();
394 continue;
395 }
396 }
397 Dmsg2(dbglvl, "No vol. index %d return false. dev=%s\n", vol_index,
398 dcr->dev->print_name());
399 break;
400 }
401 rtn = false;
402 dcr->VolumeName[0] = 0;
403
404 get_out:
405 V(vol_info_mutex);
406 unlock_volumes();
407 if (!rtn && dcr->VolCatInfo.VolScratchPoolId != 0) {
408 Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
409 Dmsg2(000, "!!!!!!!!! Volume=%s rejected ScratchPoolId=%lld\n", dcr->VolumeName,
410 dcr->VolCatInfo.VolScratchPoolId);
411 Dmsg1(000, "%s", jcr->errmsg);
412 //} else {
413 // Dmsg3(000, "Rtn=%d Volume=%s ScratchPoolId=%lld\n", rtn, dcr->VolumeName,
414 // dcr->VolCatInfo.VolScratchPoolId);
415 }
416 return rtn;
417 }
418
419
420 /*
421 * After writing a Volume, send the updated statistics
422 * back to the director. The information comes from the
423 * dev record.
424 */
dir_update_volume_info(DCR * dcr,bool label,bool update_LastWritten,bool use_dcr_only)425 bool dir_update_volume_info(DCR *dcr, bool label, bool update_LastWritten,
426 bool use_dcr_only)
427 {
428 if (askdir_handler) {
429 return askdir_handler->dir_update_volume_info(dcr, label, update_LastWritten, use_dcr_only);
430 }
431
432 JCR *jcr = dcr->jcr;
433 BSOCK *dir = jcr->dir_bsock;
434 DEVICE *dev = dcr->ameta_dev;
435 VOLUME_CAT_INFO vol;
436 char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50], ed6[50], ed7[50], ed8[50];
437 int InChanger, Enabled, Recycle;
438 bool ok = false;
439 POOL_MEM VolumeName;
440
441 /* If system job, do not update catalog, except if we explicitly force it. */
442 if (jcr->getJobType() == JT_SYSTEM &&
443 !dcr->force_update_volume_info) {
444 return true;
445 }
446
447 /* Lock during Volume update */
448 P(vol_info_mutex);
449 dev->Lock_VolCatInfo();
450
451 if (use_dcr_only) {
452 vol = dcr->VolCatInfo; /* structure assignment */
453 } else {
454 /* Just labeled or relabeled the tape */
455 if (label) {
456 dev->setVolCatStatus("Append");
457 }
458 vol = dev->VolCatInfo; /* structure assignment */
459 }
460
461 /* This happens when nothing to update after fixup_device ... */
462 if (vol.VolCatName[0] == 0) {
463 Dmsg0(50, "Volume Name is NULL\n");
464 goto bail_out;
465 }
466 Dmsg4(100, "Update cat VolBytes=%lld VolABytes=%lld Status=%s Vol=%s\n",
467 vol.VolCatAmetaBytes, vol.VolCatAdataBytes, vol.VolCatStatus, vol.VolCatName);
468 // if (update_LastWritten) {
469 vol.VolLastWritten = time(NULL);
470 // }
471 /* worm cannot be recycled, ensure catalog correct */
472 if (dev->is_worm() && vol.VolRecycle) {
473 Jmsg(jcr, M_INFO, 0, _("WORM cassette detected: setting Recycle=No on Volume=\"%s\"\n"), vol.VolCatName);
474 vol.VolRecycle = false;
475 }
476 pm_strcpy(VolumeName, vol.VolCatName);
477 bash_spaces(VolumeName);
478 InChanger = vol.InChanger;
479 Enabled = vol.VolEnabled;
480 Recycle = vol.VolRecycle;
481 /* Insanity test */
482 if (vol.VolCatHoleBytes > (((uint64_t)2)<<60)) {
483 Pmsg1(010, "VolCatHoleBytes too big: %lld. Reset to zero.\n",
484 vol.VolCatHoleBytes);
485 vol.VolCatHoleBytes = 0;
486 }
487 /* Set device type where this Volume used */
488 if (vol.VolCatType == 0) {
489 vol.VolCatType = dev->dev_type;
490 }
491
492 /* Do not lock device here because it may be locked from label */
493 if (!jcr->is_canceled()) {
494 dir->fsend(Update_media, jcr->JobId,
495 VolumeName.c_str(), vol.VolCatJobs, vol.VolCatFiles,
496 vol.VolCatBlocks, edit_uint64(vol.VolCatAmetaBytes, ed1),
497 edit_uint64(vol.VolCatAdataBytes, ed2),
498 edit_uint64(vol.VolCatHoleBytes, ed3),
499 vol.VolCatHoles, vol.VolCatMounts, vol.VolCatErrors,
500 vol.VolCatWrites, edit_uint64(vol.VolCatMaxBytes, ed4),
501 edit_uint64(vol.VolLastWritten, ed5),
502 vol.VolCatStatus, vol.Slot, label,
503 InChanger, /* bool in structure */
504 edit_int64(vol.VolReadTime, ed6),
505 edit_int64(vol.VolWriteTime, ed7),
506 edit_uint64(vol.VolFirstWritten, ed8),
507 vol.VolCatType,
508 vol.VolCatParts,
509 vol.VolCatCloudParts,
510 vol.VolLastPartBytes,
511 Enabled,
512 Recycle);
513 Dmsg1(100, ">dird %s", dir->msg);
514 /*
515 * We sent info directly from dev to the Director.
516 * What the Director sends back is first read into
517 * the dcr with do_get_volume_info()
518 */
519 if (!do_get_volume_info(dcr)) {
520 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
521 Dmsg2(dbglvl, _("Didn't get vol info vol=%s: ERR=%s"),
522 vol.VolCatName, jcr->errmsg);
523 goto bail_out;
524 }
525 Dmsg1(100, "get_volume_info() %s", dir->msg);
526
527 /* Update dev Volume info in case something changed (e.g. expired) */
528 if (!use_dcr_only) {
529 dev->VolCatInfo.Slot = dcr->VolCatInfo.Slot;
530 bstrncpy(dev->VolCatInfo.VolCatStatus, dcr->VolCatInfo.VolCatStatus, sizeof(vol.VolCatStatus));
531 dev->VolCatInfo.VolCatAdataBytes = dcr->VolCatInfo.VolCatAdataBytes;
532 dev->VolCatInfo.VolCatAmetaBytes = dcr->VolCatInfo.VolCatAmetaBytes;
533 dev->VolCatInfo.VolCatHoleBytes = dcr->VolCatInfo.VolCatHoleBytes;
534 dev->VolCatInfo.VolCatHoles = dcr->VolCatInfo.VolCatHoles;
535 dev->VolCatInfo.VolCatPadding = dcr->VolCatInfo.VolCatPadding;
536 dev->VolCatInfo.VolCatAmetaPadding = dcr->VolCatInfo.VolCatAmetaPadding;
537 dev->VolCatInfo.VolCatAdataPadding = dcr->VolCatInfo.VolCatAdataPadding;
538 dev->VolCatInfo.VolCatFiles = dcr->VolCatInfo.VolCatFiles;
539 dev->VolCatInfo.VolCatBytes = dcr->VolCatInfo.VolCatBytes;
540 dev->VolCatInfo.VolCatMounts = dcr->VolCatInfo.VolCatMounts;
541 dev->VolCatInfo.VolCatJobs = dcr->VolCatInfo.VolCatJobs;
542 dev->VolCatInfo.VolCatFiles = dcr->VolCatInfo.VolCatFiles;
543 dev->VolCatInfo.VolCatRecycles = dcr->VolCatInfo.VolCatRecycles;
544 dev->VolCatInfo.VolCatWrites = dcr->VolCatInfo.VolCatWrites;
545 dev->VolCatInfo.VolCatReads = dcr->VolCatInfo.VolCatReads;
546 dev->VolCatInfo.VolEnabled = dcr->VolCatInfo.VolEnabled;
547 dev->VolCatInfo.VolCatMaxBytes = dcr->VolCatInfo.VolCatMaxBytes;
548 dev->VolCatInfo.VolRecycle = dcr->VolCatInfo.VolRecycle;
549 }
550 ok = true;
551 }
552
553 bail_out:
554 dev->Unlock_VolCatInfo();
555 V(vol_info_mutex);
556 return ok;
557 }
558
559 struct JOBMEDIA_ITEM {
560 dlink link;
561 int64_t VolMediaId;
562 uint64_t StartAddr;
563 uint64_t EndAddr;
564 uint32_t VolFirstIndex;
565 uint32_t VolLastIndex;
566 uint32_t StartFile;
567 uint32_t EndFile;
568 uint32_t StartBlock;
569 uint32_t EndBlock;
570 };
571
create_jobmedia_queue(JCR * jcr)572 void create_jobmedia_queue(JCR *jcr)
573 {
574 JOBMEDIA_ITEM *item = NULL;
575 jcr->jobmedia_queue = New(dlist(item, &item->link));
576 }
577
flush_jobmedia_queue(JCR * jcr)578 bool flush_jobmedia_queue(JCR *jcr)
579 {
580 if (askdir_handler) {
581 return askdir_handler->flush_jobmedia_queue(jcr);
582 }
583
584 JOBMEDIA_ITEM *item;
585 BSOCK *dir = jcr->dir_bsock;
586 bool ok;
587
588 if (!jcr->jobmedia_queue || jcr->jobmedia_queue->size() == 0) {
589 return true; /* should never happen */
590 }
591 Dmsg1(400, "=== Flush jobmedia queue = %d\n", jcr->jobmedia_queue->size());
592
593 dir->fsend(Create_jobmedia, jcr->JobId);
594 foreach_dlist(item, jcr->jobmedia_queue) {
595 if (jcr->is_JobStatus(JS_Incomplete)) {
596 if (item->VolFirstIndex >= dir->get_lastFileIndex()) {
597 continue;
598 }
599 if (item->VolLastIndex >= dir->get_lastFileIndex()) {
600 item->VolLastIndex = dir->get_lastFileIndex() - 1;
601 }
602 }
603 ok = dir->fsend("%u %u %u %u %u %u %lld\n",
604 item->VolFirstIndex, item->VolLastIndex,
605 item->StartFile, item->EndFile,
606 item->StartBlock, item->EndBlock,
607 item->VolMediaId);
608 /* Keep track of last FileIndex flushed */
609 dir->set_lastFlushIndex(item->VolLastIndex);
610 Dmsg2(400, "sd->dir: ok=%d Jobmedia=%s", ok, dir->msg);
611 }
612 dir->signal(BNET_EOD);
613 jcr->jobmedia_queue->destroy();
614
615 if (dir->recv() <= 0) {
616 Dmsg0(dbglvl, "create_jobmedia error bnet_recv\n");
617 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia records: ERR=%s\n"),
618 dir->bstrerror());
619 return false;
620 }
621 Dmsg1(210, "<dird %s", dir->msg);
622 if (strcmp(dir->msg, OK_create) != 0) {
623 Dmsg1(dbglvl, "Bad response from Dir: %s\n", dir->msg);
624 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia records: %s\n"), dir->msg);
625 return false;
626 }
627 return true;
628 }
629
630
631 /*
632 * After writing a Volume, create the JobMedia record.
633 */
dir_create_jobmedia_record(DCR * dcr,bool zero)634 bool dir_create_jobmedia_record(DCR *dcr, bool zero)
635 {
636 if (askdir_handler) {
637 return askdir_handler->dir_create_jobmedia_record(dcr, zero);
638 }
639
640 JCR *jcr = dcr->jcr;
641 BSOCK *dir = jcr->dir_bsock;
642 JOBMEDIA_ITEM *item;
643 bool ok = true;;
644
645 if (!zero && !dcr->WroteVol) {
646 return true;
647 }
648 if (!zero && dcr->VolLastIndex == 0) {
649 Pmsg7(0/*dbglvl*/, "Discard: JobMedia Vol=%s wrote=%d MediaId=%lld FI=%lu LI=%lu StartAddr=%lld EndAddr=%lld\n",
650 dcr->VolumeName, dcr->WroteVol, dcr->VolMediaId,
651 dcr->VolFirstIndex, dcr->VolLastIndex, dcr->StartAddr, dcr->EndAddr);
652 return true; /* nothing written to the Volume */
653 }
654 /* Throw out records where the start address is bigger than the end */
655 if (!zero && dcr->StartAddr > dcr->EndAddr) {
656 Pmsg7(0/*dbglvl*/, "Discard: JobMedia Vol=%s wrote=%d MediaId=%lld FI=%lu LI=%lu StartAddr=%lld EndAddr=%lld\n",
657 dcr->VolumeName, dcr->WroteVol, dcr->VolMediaId,
658 dcr->VolFirstIndex, dcr->VolLastIndex, dcr->StartAddr, dcr->EndAddr);
659 return true;
660 }
661
662 /* If system job, do not update catalog */
663 if (jcr->getJobType() == JT_SYSTEM) {
664 return true;
665 }
666
667 /* Throw out records where FI is zero -- i.e. nothing done */
668 if (!zero && dcr->VolFirstIndex == 0 &&
669 (dcr->StartAddr != 0 || dcr->EndAddr != 0)) {
670 Pmsg7(0/*dbglvl*/, "Discard: JobMedia Vol=%s wrote=%d MediaId=%lld FI=%lu LI=%lu StartAddr=%lld EndAddr=%lld\n",
671 dcr->VolumeName, dcr->WroteVol, dcr->VolMediaId,
672 dcr->VolFirstIndex, dcr->VolLastIndex, dcr->StartAddr, dcr->EndAddr);
673 return true;
674 }
675
676 /*
677 * If this Job is incomplete, we need to backup the FileIndex
678 * to the last correctly saved file so that the JobMedia
679 * LastIndex is correct.
680 *
681 */
682 if (jcr->is_JobStatus(JS_Incomplete)) {
683 dcr->VolLastIndex = dir->get_lastFileIndex();
684 Dmsg1(100, "======= Set FI=%ld\n", dcr->VolLastIndex);
685 }
686
687 Dmsg7(100, "Queue JobMedia Vol=%s wrote=%d MediaId=%lld FI=%lu LI=%lu StartAddr=%lld EndAddr=%lld\n",
688 dcr->VolumeName, dcr->WroteVol, dcr->VolMediaId,
689 dcr->VolFirstIndex, dcr->VolLastIndex, dcr->StartAddr, dcr->EndAddr);
690 item = (JOBMEDIA_ITEM *)malloc(sizeof(JOBMEDIA_ITEM));
691 if (zero) {
692 item->VolFirstIndex = item->VolLastIndex = 0;
693 item->StartFile = item->EndFile = 0;
694 item->StartBlock = item->EndBlock = 0;
695 item->StartAddr = item->EndAddr = 0;
696 item->VolMediaId = dcr->VolMediaId;
697 } else {
698 item->VolFirstIndex = dcr->VolFirstIndex;
699 item->VolLastIndex = dcr->VolLastIndex;
700 item->StartFile = (uint32_t)(dcr->StartAddr >> 32);
701 item->EndFile = (uint32_t)(dcr->EndAddr >> 32);
702 item->StartBlock = (uint32_t)dcr->StartAddr;
703 item->EndBlock = (uint32_t)dcr->EndAddr;
704 item->StartAddr = dcr->StartAddr;
705 item->EndAddr = dcr->EndAddr;
706 item->VolMediaId = dcr->VolMediaId;
707 }
708 jcr->jobmedia_queue->append(item);
709 /* Flush at queue size of 1000 jobmedia records */
710 if (zero || jcr->jobmedia_queue->size() >= 1000) {
711 ok = flush_jobmedia_queue(jcr);
712 }
713
714 dcr->VolFirstIndex = dcr->VolLastIndex = 0;
715 dcr->StartAddr = dcr->EndAddr = 0;
716 dcr->VolMediaId = 0;
717 dcr->WroteVol = false;
718 return ok;
719 }
720
721 /*
722 * Update File Attribute data
723 * We do the following:
724 * 1. expand the bsock buffer to be large enough
725 * 2. Write a "header" into the buffer with serialized data
726 * VolSessionId
727 * VolSeesionTime
728 * FileIndex
729 * Stream
730 * data length that follows
731 * start of raw byte data from the Device record.
732 * Note, this is primarily for Attribute data, but can
733 * also handle any device record. The Director must know
734 * the raw byte data format that is defined for each Stream.
735 * Now Restore Objects pass through here STREAM_RESTORE_OBJECT
736 */
dir_update_file_attributes(DCR * dcr,DEV_RECORD * rec)737 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
738 {
739 if (askdir_handler) {
740 return askdir_handler->dir_update_file_attributes(dcr, rec);
741 }
742
743 JCR *jcr = dcr->jcr;
744 BSOCK *dir = jcr->dir_bsock;
745 ser_declare;
746
747 #ifdef NO_ATTRIBUTES_TEST
748 return true;
749 #endif
750
751 dir->msg = check_pool_memory_size(dir->msg, sizeof(FileAttributes) +
752 MAX_NAME_LENGTH + sizeof(DEV_RECORD) + rec->data_len + 1);
753 dir->msglen = bsnprintf(dir->msg, sizeof(FileAttributes) +
754 MAX_NAME_LENGTH + 1, FileAttributes, jcr->JobId);
755 ser_begin(dir->msg + dir->msglen, 0);
756 ser_uint32(rec->VolSessionId);
757 ser_uint32(rec->VolSessionTime);
758 ser_int32(rec->FileIndex);
759 ser_int32(rec->Stream);
760 ser_uint32(rec->data_len);
761 ser_bytes(rec->data, rec->data_len);
762 dir->msglen = ser_length(dir->msg);
763 Dmsg1(1800, ">dird %s\n", dir->msg); /* Attributes */
764 if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES ||
765 rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX) {
766 Dmsg2(1500, "==== set_data_end FI=%ld %s\n", rec->FileIndex, rec->data);
767 dir->set_data_end(rec->FileIndex); /* set offset of valid data */
768 }
769 return dir->send();
770 }
771
772
773 /**
774 * Request the sysop to create an appendable volume
775 *
776 * Entered with device blocked.
777 * Leaves with device blocked.
778 *
779 * Returns: true on success (operator issues a mount command)
780 * false on failure
781 * Note, must create dev->errmsg on error return.
782 *
783 * On success, dcr->VolumeName and dcr->VolCatInfo contain
784 * information on suggested volume, but this may not be the
785 * same as what is actually mounted.
786 *
787 * When we return with success, the correct tape may or may not
788 * actually be mounted. The calling routine must read it and
789 * verify the label.
790 */
dir_ask_sysop_to_create_appendable_volume(DCR * dcr)791 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
792 {
793 if (askdir_handler) {
794 return askdir_handler->dir_ask_sysop_to_create_appendable_volume(dcr);
795 }
796
797 int stat = W_TIMEOUT;
798 DEVICE *dev = dcr->dev;
799 JCR *jcr = dcr->jcr;
800 bool got_vol = false;
801
802 if (job_canceled(jcr)) {
803 dev->poll = false;
804 return false;
805 }
806 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
807 ASSERT(dev->blocked());
808 for ( ;; ) {
809 if (job_canceled(jcr)) {
810 Mmsg(dev->errmsg,
811 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
812 jcr->Job, dev->print_name());
813 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
814 dev->poll = false;
815 return false;
816 }
817 got_vol = dir_find_next_appendable_volume(dcr); /* get suggested volume */
818 if (got_vol) {
819 goto get_out;
820 } else {
821 dev->clear_wait();
822 if (stat == W_TIMEOUT || stat == W_MOUNT) {
823 Mmsg(dev->errmsg, _(
824 "Job %s is waiting. Cannot find any appendable volumes.\n"
825 "Please use the \"label\" command to create a new Volume for:\n"
826 " Storage: %s\n"
827 " Pool: %s\n"
828 " Media type: %s\n"),
829 jcr->Job,
830 dev->print_name(),
831 dcr->pool_name,
832 dcr->media_type);
833 Jmsg(jcr, M_MOUNT, 0, "%s", dev->errmsg);
834 Dmsg1(dbglvl, "%s", dev->errmsg);
835 }
836 }
837
838 jcr->sendJobStatus(JS_WaitMedia);
839
840 stat = wait_for_sysop(dcr);
841 Dmsg1(dbglvl, "Back from wait_for_sysop stat=%d\n", stat);
842 if (dev->poll) {
843 Dmsg1(dbglvl, "Poll timeout in create append vol on device %s\n", dev->print_name());
844 continue;
845 }
846
847 if (stat == W_TIMEOUT) {
848 if (!double_dev_wait_time(dev)) {
849 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
850 dev->print_name(), jcr->Job);
851 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
852 Dmsg1(dbglvl, "Gave up waiting on device %s\n", dev->print_name());
853 dev->poll = false;
854 return false; /* exceeded maximum waits */
855 }
856 continue;
857 }
858 if (stat == W_ERROR) {
859 berrno be;
860 Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
861 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
862 dev->poll = false;
863 return false;
864 }
865 Dmsg1(dbglvl, "Someone woke me for device %s\n", dev->print_name());
866 }
867
868 get_out:
869 jcr->sendJobStatus(JS_Running);
870 Dmsg0(dbglvl, "leave dir_ask_sysop_to_create_appendable_volume\n");
871 return true;
872 }
873
874 /*
875 * Request to mount specific Volume
876 *
877 * Entered with device blocked and dcr->VolumeName is desired
878 * volume.
879 * Leaves with device blocked.
880 *
881 * Returns: true on success (operator issues a mount command)
882 * false on failure
883 * Note, must create dev->errmsg on error return.
884 *
885 */
dir_ask_sysop_to_mount_volume(DCR * dcr,bool write_access)886 bool dir_ask_sysop_to_mount_volume(DCR *dcr, bool write_access)
887 {
888 if (askdir_handler) {
889 return askdir_handler->dir_ask_sysop_to_mount_volume(dcr, write_access);
890 }
891
892 int stat = W_TIMEOUT;
893 DEVICE *dev = dcr->dev;
894 JCR *jcr = dcr->jcr;
895
896 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
897 if (!dcr->VolumeName[0]) {
898 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
899 dev->poll = false;
900 return false;
901 }
902
903 if (dcr->no_mount_request) {
904 Mmsg(dev->errmsg, _("The current operation doesn't support mount request\n"));
905 dev->poll = false;
906 return false;
907 }
908
909 for ( ;; ) {
910 if (job_canceled(jcr)) {
911 Mmsg(dev->errmsg,
912 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
913 jcr->Job, dev->print_name());
914 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
915 dev->poll = false;
916 return false;
917 }
918 /*
919 * If we are not polling, and the wait timeout or the
920 * user explicitly did a mount, send him the message.
921 * Otherwise skip it.
922 */
923 if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
924 const char *msg;
925 if (write_access) {
926 msg = _("%sPlease mount append Volume \"%s\" or label a new one for:\n"
927 " Job: %s\n"
928 " Storage: %s\n"
929 " Pool: %s\n"
930 " Media type: %s\n");
931 } else {
932 msg = _("%sPlease mount read Volume \"%s\" for:\n"
933 " Job: %s\n"
934 " Storage: %s\n"
935 " Pool: %s\n"
936 " Media type: %s\n");
937 }
938 Jmsg(jcr, M_MOUNT, 0, msg,
939 dev->is_nospace()?_("\n\nWARNING: device is full! Please add more disk space then ...\n\n"):"",
940 dcr->VolumeName,
941 jcr->Job,
942 dev->print_name(),
943 dcr->pool_name,
944 dcr->media_type);
945 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
946 dcr->VolumeName, dev->print_name(), jcr->Job);
947 }
948
949 jcr->sendJobStatus(JS_WaitMount);
950
951 stat = wait_for_sysop(dcr); /* wait on device */
952 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
953 if (dev->poll) {
954 Dmsg1(100, "Poll timeout in mount vol on device %s\n", dev->print_name());
955 Dmsg1(100, "Blocked=%s\n", dev->print_blocked());
956 goto get_out;
957 }
958
959 if (stat == W_TIMEOUT) {
960 if (!double_dev_wait_time(dev)) {
961 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
962 dev->print_name(), jcr->Job);
963 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
964 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
965 dev->poll = false;
966 return false; /* exceeded maximum waits */
967 }
968 continue;
969 }
970 if (stat == W_ERROR) {
971 berrno be;
972 Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
973 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
974 dev->poll = false;
975 return false;
976 }
977 Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
978 break;
979 }
980
981 get_out:
982 if (job_canceled(jcr)) {
983 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
984 jcr->Job, dev->print_name());
985 dev->poll = false;
986 return false;
987 }
988
989 jcr->sendJobStatus(JS_Running);
990 Dmsg0(100, "leave dir_ask_sysop_to_mount_volume\n");
991 return true;
992 }
993