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