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