1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2003-2012 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2016 Planets Communications B.V.
6    Copyright (C) 2013-2019 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22 */
23 /*
24  * Kern Sibbald, April MMIII
25  */
26 /**
27  * @file
28  * BAREOS Director -- Tape labeling commands
29  */
30 
31 #include "include/bareos.h"
32 #include "dird.h"
33 #include "dird/dird_globals.h"
34 #include "dird/msgchan.h"
35 #include "dird/next_vol.h"
36 
37 #if HAVE_NDMP
38 #include "ndmp/ndmagents.h"
39 #endif
40 
41 #include "dird/ndmp_dma_storage.h"
42 #include "dird/jcr_private.h"
43 #include "dird/sd_cmds.h"
44 #include "dird/storage.h"
45 #include "dird/ua_db.h"
46 #include "dird/ua_input.h"
47 #include "dird/ua_label.h"
48 #include "dird/ua_select.h"
49 #include "include/auth_protocol_types.h"
50 #include "lib/crypto_wrap.h"
51 #include "lib/passphrase.h"
52 #include "lib/util.h"
53 
54 namespace directordaemon {
55 
56 /* Forward referenced functions */
57 
58 /* External functions */
59 
update_database(UaContext * ua,MediaDbRecord * mr,PoolDbRecord * pr,bool media_record_exists)60 static inline bool update_database(UaContext* ua,
61                                    MediaDbRecord* mr,
62                                    PoolDbRecord* pr,
63                                    bool media_record_exists)
64 {
65   bool retval = true;
66 
67   if (media_record_exists) {
68     /*
69      * Update existing media record.
70      */
71     mr->InChanger = mr->Slot > 0; /* If slot give assume in changer */
72     SetStorageidInMr(ua->jcr->impl->res.write_storage, mr);
73     if (!ua->db->UpdateMediaRecord(ua->jcr, mr)) {
74       ua->ErrorMsg("%s", ua->db->strerror());
75       retval = false;
76     }
77   } else {
78     /*
79      * Create the media record
80      */
81     SetPoolDbrDefaultsInMediaDbr(mr, pr);
82     mr->InChanger = mr->Slot > 0; /* If slot give assume in changer */
83     mr->Enabled = 1;
84     SetStorageidInMr(ua->jcr->impl->res.write_storage, mr);
85 
86     if (ua->db->CreateMediaRecord(ua->jcr, mr)) {
87       ua->InfoMsg(_("Catalog record for Volume \"%s\", Slot %hd successfully "
88                     "created.\n"),
89                   mr->VolumeName, mr->Slot);
90 
91       /*
92        * Update number of volumes in pool
93        */
94       pr->NumVols++;
95       if (!ua->db->UpdatePoolRecord(ua->jcr, pr)) {
96         ua->ErrorMsg("%s", ua->db->strerror());
97       }
98     } else {
99       ua->ErrorMsg("%s", ua->db->strerror());
100       retval = false;
101     }
102   }
103 
104   return retval;
105 }
106 
107 /*
108  * NOTE! This routine opens the SD socket but leaves it open
109  */
native_send_label_request(UaContext * ua,MediaDbRecord * mr,MediaDbRecord * omr,PoolDbRecord * pr,bool relabel,drive_number_t drive)110 static inline bool native_send_label_request(UaContext* ua,
111                                              MediaDbRecord* mr,
112                                              MediaDbRecord* omr,
113                                              PoolDbRecord* pr,
114                                              bool relabel,
115                                              drive_number_t drive)
116 {
117   BareosSocket* sd;
118   char dev_name[MAX_NAME_LENGTH];
119   bool retval = false;
120   uint64_t VolBytes = 0;
121 
122   if (!(sd = open_sd_bsock(ua))) { return false; }
123 
124   bstrncpy(dev_name, ua->jcr->impl->res.write_storage->dev_name(),
125            sizeof(dev_name));
126   BashSpaces(dev_name);
127   BashSpaces(mr->VolumeName);
128   BashSpaces(mr->MediaType);
129   BashSpaces(pr->Name);
130 
131   if (relabel) {
132     BashSpaces(omr->VolumeName);
133     sd->fsend(
134         "relabel %s OldName=%s NewName=%s PoolName=%s "
135         "MediaType=%s Slot=%hd drive=%hd MinBlocksize=%d MaxBlocksize=%d",
136         dev_name, omr->VolumeName, mr->VolumeName, pr->Name, mr->MediaType,
137         mr->Slot, drive,
138         /*
139          * if relabeling, keep blocksize settings
140          */
141         omr->MinBlocksize, omr->MaxBlocksize);
142     ua->SendMsg(_("Sending relabel command from \"%s\" to \"%s\" ...\n"),
143                 omr->VolumeName, mr->VolumeName);
144   } else {
145     sd->fsend(
146         "label %s VolumeName=%s PoolName=%s MediaType=%s "
147         "Slot=%hd drive=%hd MinBlocksize=%d MaxBlocksize=%d",
148         dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive,
149         /*
150          * If labeling, use blocksize defined in pool
151          */
152         pr->MinBlocksize, pr->MaxBlocksize);
153     ua->SendMsg(_("Sending label command for Volume \"%s\" Slot %hd ...\n"),
154                 mr->VolumeName, mr->Slot);
155     Dmsg8(100,
156           "label %s VolumeName=%s PoolName=%s MediaType=%s "
157           "Slot=%hd drive=%hd MinBlocksize=%d MaxBlocksize=%d\n",
158           dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive,
159           pr->MinBlocksize, pr->MaxBlocksize);
160   }
161 
162   /*
163    * We use BgetDirmsg here and not BnetRecv because as part of
164    * the label request the stored can request catalog information for
165    * any plugin who listens to the bsdEventLabelVerified event.
166    * As we don't want to loose any non protocol data e.g. errors
167    * without a 3xxx prefix we set the allow_any_message of
168    * BgetDirmsg to true and as such is behaves like a normal
169    * BnetRecv for any non protocol messages.
170    */
171   while (BgetDirmsg(sd, true) >= 0) {
172     ua->SendMsg("%s", sd->msg);
173     if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu ", &VolBytes) == 1) {
174       retval = true;
175     }
176   }
177 
178   UnbashSpaces(mr->VolumeName);
179   UnbashSpaces(mr->MediaType);
180   UnbashSpaces(pr->Name);
181   mr->LabelDate = time(NULL);
182   mr->set_label_date = true;
183   mr->VolBytes = VolBytes;
184 
185   return retval;
186 }
187 
188 /*
189  * Generate a new encryption key for use in volume encryption.
190  * We don't ask the user for a encryption key but generate a semi
191  * random passphrase of the wanted length which is way stronger.
192  * When the config has a wrap key we use that to wrap the newly
193  * created passphrase using RFC3394 aes wrap and always convert
194  * the passphrase into a base64 encoded string. This key is
195  * stored in the database and is passed to the storage daemon
196  * when needed. The storage daemon has the same wrap key per
197  * director so it can unwrap the passphrase for use.
198  *
199  * Changing the wrap key will render any previously created
200  * crypto keys useless so only change the wrap key after initial
201  * setting when you know what you are doing and always store
202  * the old key somewhere save so you can use bscrypto to
203  * convert them for the new wrap key.
204  */
GenerateNewEncryptionKey(UaContext * ua,MediaDbRecord * mr)205 static bool GenerateNewEncryptionKey(UaContext* ua, MediaDbRecord* mr)
206 {
207   int length;
208   char* passphrase;
209 
210   passphrase = generate_crypto_passphrase(DEFAULT_PASSPHRASE_LENGTH);
211   if (!passphrase) {
212     ua->ErrorMsg(
213         _("Failed to generate new encryption passphrase for Volume %s.\n"),
214         mr->VolumeName);
215     return false;
216   }
217 
218   /*
219    * See if we need to wrap the passphrase.
220    */
221   if (me->keyencrkey.value) {
222     char* wrapped_passphrase;
223 
224     length = DEFAULT_PASSPHRASE_LENGTH + 8;
225     wrapped_passphrase = (char*)malloc(length);
226     memset(wrapped_passphrase, 0, length);
227     AesWrap((unsigned char*)me->keyencrkey.value, DEFAULT_PASSPHRASE_LENGTH / 8,
228             (unsigned char*)passphrase, (unsigned char*)wrapped_passphrase);
229 
230     free(passphrase);
231     passphrase = wrapped_passphrase;
232   } else {
233     length = DEFAULT_PASSPHRASE_LENGTH;
234   }
235 
236   /*
237    * The passphrase is always base64 encoded.
238    */
239   BinToBase64(mr->EncrKey, sizeof(mr->EncrKey), passphrase, length, true);
240 
241   free(passphrase);
242   return true;
243 }
244 
SendLabelRequest(UaContext * ua,StorageResource * store,MediaDbRecord * mr,MediaDbRecord * omr,PoolDbRecord * pr,bool media_record_exists,bool relabel,drive_number_t drive,slot_number_t slot)245 bool SendLabelRequest(UaContext* ua,
246                       StorageResource* store,
247                       MediaDbRecord* mr,
248                       MediaDbRecord* omr,
249                       PoolDbRecord* pr,
250                       bool media_record_exists,
251                       bool relabel,
252                       drive_number_t drive,
253                       slot_number_t slot)
254 {
255   bool retval;
256 
257   switch (store->Protocol) {
258     case APT_NATIVE:
259       retval = native_send_label_request(ua, mr, omr, pr, relabel, drive);
260       break;
261     case APT_NDMPV2:
262     case APT_NDMPV3:
263     case APT_NDMPV4:
264       retval =
265           NdmpSendLabelRequest(ua, store, mr, omr, pr, relabel, drive, slot);
266       break;
267     default:
268       retval = false;
269       break;
270   }
271 
272   if (retval) {
273     retval = update_database(ua, mr, pr, media_record_exists);
274   } else {
275     ua->ErrorMsg(_("Label command failed for Volume %s.\n"), mr->VolumeName);
276   }
277 
278   return retval;
279 }
280 
281 /*
282  * Check if this is a cleaning tape by comparing the Volume name
283  * with the Cleaning Prefix. If they match, this is a cleaning tape.
284  */
IsCleaningTape(UaContext * ua,MediaDbRecord * mr,PoolDbRecord * pr)285 static inline bool IsCleaningTape(UaContext* ua,
286                                   MediaDbRecord* mr,
287                                   PoolDbRecord* pr)
288 {
289   bool retval;
290 
291   /*
292    * Find Pool resource
293    */
294   ua->jcr->impl->res.pool = ua->GetPoolResWithName(pr->Name, false);
295   if (!ua->jcr->impl->res.pool) {
296     ua->ErrorMsg(_("Pool \"%s\" resource not found for volume \"%s\"!\n"),
297                  pr->Name, mr->VolumeName);
298     return false;
299   }
300 
301   retval = bstrncmp(mr->VolumeName, ua->jcr->impl->res.pool->cleaning_prefix,
302                     strlen(ua->jcr->impl->res.pool->cleaning_prefix));
303 
304   Dmsg4(100, "CLNprefix=%s: Vol=%s: len=%d bstrncmp=%s\n",
305         ua->jcr->impl->res.pool->cleaning_prefix, mr->VolumeName,
306         strlen(ua->jcr->impl->res.pool->cleaning_prefix),
307         retval ? "true" : "false");
308 
309   return retval;
310 }
311 
312 
313 /*
314  * Request Storage to send us the slot:barcodes, then wiffle through them all
315  * labeling them.
316  */
label_from_barcodes(UaContext * ua,drive_number_t drive,bool label_encrypt,bool yes)317 static void label_from_barcodes(UaContext* ua,
318                                 drive_number_t drive,
319                                 bool label_encrypt,
320                                 bool yes)
321 {
322   StorageResource* store = ua->jcr->impl->res.write_storage;
323   PoolDbRecord pr;
324   MediaDbRecord mr;
325   vol_list_t* vl;
326   changer_vol_list_t* vol_list = NULL;
327   bool media_record_exists;
328   char* slot_list;
329   int max_slots;
330 
331 
332   max_slots = GetNumSlots(ua, ua->jcr->impl->res.write_storage);
333   if (max_slots <= 0) {
334     ua->WarningMsg(_("No slots in changer to scan.\n"));
335     return;
336   }
337 
338   slot_list = (char*)malloc(NbytesForBits(max_slots));
339   ClearAllBits(max_slots, slot_list);
340   if (!GetUserSlotList(ua, slot_list, "slots", max_slots)) { goto bail_out; }
341 
342   vol_list = get_vol_list_from_storage(ua, store, false /* no listall */,
343                                        false /* no scan */);
344   if (!vol_list) {
345     ua->WarningMsg(_("No Volumes found to label, or no barcodes.\n"));
346     goto bail_out;
347   }
348 
349   /*
350    * Display list of Volumes and ask if he really wants to proceed
351    */
352   ua->SendMsg(
353       _("The following Volumes will be labeled:\n"
354         "Slot  Volume\n"
355         "==============\n"));
356   foreach_dlist (vl, vol_list->contents) {
357     if (!vl->VolName || !BitIsSet(vl->bareos_slot_number - 1, slot_list)) {
358       continue;
359     }
360     ua->SendMsg("%4d  %s\n", vl->bareos_slot_number, vl->VolName);
361   }
362 
363   if (!yes &&
364       (!GetYesno(ua, _("Do you want to label these Volumes? (yes|no): ")) ||
365        !ua->pint32_val)) {
366     goto bail_out;
367   }
368 
369   /*
370    * Select a pool
371    */
372   if (!SelectPoolDbr(ua, &pr)) { goto bail_out; }
373 
374   /*
375    * Fire off the label requests
376    */
377   foreach_dlist (vl, vol_list->contents) {
378     if (!vl->VolName || !BitIsSet(vl->bareos_slot_number - 1, slot_list)) {
379       continue;
380     }
381     mr = MediaDbRecord{};
382     bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
383     media_record_exists = false;
384     if (ua->db->GetMediaRecord(ua->jcr, &mr)) {
385       if (mr.VolBytes != 0) {
386         ua->WarningMsg(
387             _("Media record for Slot %hd Volume \"%s\" already exists.\n"),
388             vl->bareos_slot_number, mr.VolumeName);
389         mr.Slot = vl->bareos_slot_number;
390         mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
391         SetStorageidInMr(store, &mr);
392         if (!ua->db->UpdateMediaRecord(ua->jcr, &mr)) {
393           ua->ErrorMsg(_("Error setting InChanger: ERR=%s"),
394                        ua->db->strerror());
395         }
396         continue;
397       }
398       media_record_exists = true;
399     }
400     mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
401     SetStorageidInMr(store, &mr);
402 
403     /*
404      * Deal with creating cleaning tape here.
405      * Normal tapes created in SendLabelRequest() below
406      */
407     if (IsCleaningTape(ua, &mr, &pr)) {
408       if (media_record_exists) { /* we update it */
409         mr.VolBytes = 1;         /* any bytes to indicate it exists */
410         bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
411         mr.MediaType[0] = 0;
412         SetStorageidInMr(store, &mr);
413         if (!ua->db->UpdateMediaRecord(ua->jcr, &mr)) {
414           ua->ErrorMsg("%s", ua->db->strerror());
415         }
416       } else { /* create the media record */
417         if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
418           ua->ErrorMsg(_("Maximum pool Volumes=%d reached.\n"), pr.MaxVols);
419           goto bail_out;
420         }
421         SetPoolDbrDefaultsInMediaDbr(&mr, &pr);
422         bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
423         mr.MediaType[0] = 0;
424         SetStorageidInMr(store, &mr);
425         if (ua->db->CreateMediaRecord(ua->jcr, &mr)) {
426           ua->SendMsg(_("Catalog record for cleaning tape \"%s\" successfully "
427                         "created.\n"),
428                       mr.VolumeName);
429           pr.NumVols++; /* this is a bit suspect */
430           if (!ua->db->UpdatePoolRecord(ua->jcr, &pr)) {
431             ua->ErrorMsg("%s", ua->db->strerror());
432           }
433         } else {
434           ua->ErrorMsg(_("Catalog error on cleaning tape: %s"),
435                        ua->db->strerror());
436         }
437       }
438       continue; /* done, go handle next volume */
439     }
440     bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
441 
442     /*
443      * See if we need to generate a new passphrase for hardware encryption.
444      */
445     if (label_encrypt) {
446       if (!GenerateNewEncryptionKey(ua, &mr)) { continue; }
447     }
448 
449     mr.Slot = vl->bareos_slot_number;
450     SendLabelRequest(ua, store, &mr, NULL, &pr, media_record_exists, false,
451                      drive, vl->bareos_slot_number);
452   }
453 
454 bail_out:
455   free(slot_list);
456   if (vol_list) { StorageReleaseVolList(store, vol_list); }
457   CloseSdBsock(ua);
458 
459   return;
460 }
461 
462 /*
463  * Common routine for both label and relabel
464  */
do_label(UaContext * ua,const char * cmd,bool relabel)465 static int do_label(UaContext* ua, const char* cmd, bool relabel)
466 {
467   int i, j;
468   BareosSocket* sd;
469   MediaDbRecord mr, omr;
470   PoolDbRecord pr;
471   UnifiedStorageResource store;
472   bool ok = false;
473   bool yes = false; /* Was "yes" given on cmdline */
474   drive_number_t drive;
475   bool print_reminder = true;
476   bool label_barcodes = false;
477   bool label_encrypt = false;
478   bool media_record_exists = false;
479   char dev_name[MAX_NAME_LENGTH];
480   static const char* barcode_keywords[] = {"barcode", "barcodes", NULL};
481 
482   if (!OpenClientDb(ua)) { return 1; }
483 
484   if (ua->batch || FindArg(ua, NT_("yes")) > 0) { yes = true; }
485 
486   /*
487    * Look for one of the barcode keywords
488    */
489   if (!relabel && (i = FindArgKeyword(ua, barcode_keywords)) >= 0) {
490     /*
491      * Now find the keyword in the list
492      */
493     if ((j = FindArg(ua, barcode_keywords[i])) > 0) {
494       *ua->argk[j] = 0; /* zap barcode keyword */
495     }
496     label_barcodes = true;
497   }
498 
499   /*
500    * Look for the encrypt keyword
501    */
502   if ((i = FindArg(ua, "encrypt")) > 0) {
503     *ua->argk[i] = 0; /* zap encrypt keyword */
504     label_encrypt = true;
505   }
506 
507   store.store = get_storage_resource(ua, true, label_barcodes);
508   if (!store.store) { return 1; }
509 
510   switch (store.store->Protocol) {
511     case APT_NDMPV2:
512     case APT_NDMPV3:
513     case APT_NDMPV4:
514       /*
515        * See if the user selected a NDMP storage device but its
516        * handled by a native Bareos storage daemon e.g. we have
517        * a paired_storage pointer.
518        */
519       if (store.store->paired_storage) {
520         store.store = store.store->paired_storage;
521       }
522       break;
523     default:
524       break;
525   }
526 
527   PmStrcpy(store.store_source, _("command line"));
528   SetWstorage(ua->jcr, &store);
529   drive = GetStorageDrive(ua, store.store);
530 
531   if (label_barcodes) {
532     label_from_barcodes(ua, drive, label_encrypt, yes);
533     return 1;
534   }
535 
536   /*
537    * If relabel get name of Volume to relabel
538    */
539   if (relabel) {
540     /*
541      * Check for oldvolume=name
542      */
543     i = FindArgWithValue(ua, "oldvolume");
544     if (i >= 0) {
545       bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
546       if (ua->db->GetMediaRecord(ua->jcr, &omr)) { goto checkVol; }
547       ua->ErrorMsg("%s", ua->db->strerror());
548     }
549     /*
550      * No keyword or Vol not found, ask user to select
551      */
552     if (!SelectMediaDbr(ua, &omr)) { return 1; }
553 
554     /*
555      * Require Volume to be Purged or Recycled
556      */
557   checkVol:
558     if (!bstrcmp(omr.VolStatus, "Purged") &&
559         !bstrcmp(omr.VolStatus, "Recycle")) {
560       ua->ErrorMsg(_("Volume \"%s\" has VolStatus %s. It must be Purged or "
561                      "Recycled before relabeling.\n"),
562                    omr.VolumeName, omr.VolStatus);
563       return 1;
564     }
565   }
566 
567   /*
568    * Check for volume=NewVolume
569    */
570   i = FindArgWithValue(ua, "volume");
571   if (i >= 0) {
572     PmStrcpy(ua->cmd, ua->argv[i]);
573     goto checkName;
574   }
575 
576   /*
577    * Get a new Volume name
578    */
579   for (;;) {
580     media_record_exists = false;
581     if (!GetCmd(ua, _("Enter new Volume name: "))) { return 1; }
582   checkName:
583     if (!IsVolumeNameLegal(ua, ua->cmd)) { continue; }
584 
585     /*
586      * Search by Media name so set VolumeName and clear MediaId.
587      */
588     mr.MediaId = 0;
589     bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
590 
591     /*
592      * If VolBytes are zero the Volume is not labeled
593      */
594     if (ua->db->GetMediaRecord(ua->jcr, &mr)) {
595       if (mr.VolBytes != 0) {
596         ua->ErrorMsg(_("Media record for new Volume \"%s\" already exists.\n"),
597                      mr.VolumeName);
598         continue;
599       }
600       media_record_exists = true;
601     }
602     break; /* Got it */
603   }
604 
605   /*
606    * If autochanger, request slot
607    */
608   i = FindArgWithValue(ua, "slot");
609   if (i >= 0) {
610     mr.Slot = atoi(ua->argv[i]);
611     if (mr.Slot < 0) { mr.Slot = 0; }
612     mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
613   } else if (store.store->autochanger) {
614     if (!GetPint(ua, _("Enter slot (0 or Enter for none): "))) { return 1; }
615     mr.Slot = (slot_number_t)ua->pint32_val;
616     if (mr.Slot < 0) { mr.Slot = 0; }
617     mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
618   }
619   SetStorageidInMr(store.store, &mr);
620 
621   bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType));
622 
623   /*
624    * Must select Pool if not already done
625    */
626   if (pr.PoolId == 0) {
627     pr = PoolDbRecord{};
628     if (!SelectPoolDbr(ua, &pr)) { return 1; }
629   }
630 
631   /*
632    * See if we need to generate a new passphrase for hardware encryption.
633    */
634   if (label_encrypt) {
635     ua->InfoMsg(_("Generating new hardware encryption key\n"));
636     if (!GenerateNewEncryptionKey(ua, &mr)) { return 1; }
637   }
638 
639   ok = SendLabelRequest(ua, store.store, &mr, &omr, &pr, media_record_exists,
640                         relabel, drive, mr.Slot);
641   if (ok) {
642     sd = ua->jcr->store_bsock;
643     if (relabel) {
644       /*
645        * Delete the old media record
646        */
647       if (!ua->db->DeleteMediaRecord(ua->jcr, &omr)) {
648         ua->ErrorMsg(_("Delete of Volume \"%s\" failed. ERR=%s"),
649                      omr.VolumeName, ua->db->strerror());
650       } else {
651         ua->InfoMsg(_("Old volume \"%s\" deleted from catalog.\n"),
652                     omr.VolumeName);
653         /*
654          * Update the number of Volumes in the pool
655          */
656         pr.NumVols--;
657         if (!ua->db->UpdatePoolRecord(ua->jcr, &pr)) {
658           ua->ErrorMsg("%s", ua->db->strerror());
659         }
660       }
661     }
662     if (ua->automount) {
663       bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
664       ua->InfoMsg(_("Requesting to mount %s ...\n"), dev_name);
665       BashSpaces(dev_name);
666       sd->fsend("mount %s drive=%hd", dev_name, drive);
667       UnbashSpaces(dev_name);
668 
669       /*
670        * We use BgetDirmsg here and not BnetRecv because as part of
671        * the mount request the stored can request catalog information for
672        * any plugin who listens to the bsdEventLabelVerified event.
673        * As we don't want to loose any non protocol data e.g. errors
674        * without a 3xxx prefix we set the allow_any_message of
675        * BgetDirmsg to true and as such is behaves like a normal
676        * BnetRecv for any non protocol messages.
677        */
678       while (BgetDirmsg(sd, true) >= 0) {
679         ua->SendMsg("%s", sd->msg);
680 
681         /*
682          * Here we can get
683          *  3001 OK mount. Device=xxx      or
684          *  3001 Mounted Volume vvvv
685          *  3002 Device "DVD-Writer" (/dev/hdc) is mounted.
686          *  3906 is cannot mount non-tape
687          * So for those, no need to print a reminder
688          */
689         if (bstrncmp(sd->msg, "3001 ", 5) || bstrncmp(sd->msg, "3002 ", 5) ||
690             bstrncmp(sd->msg, "3906 ", 5)) {
691           print_reminder = false;
692         }
693       }
694     }
695   }
696 
697   if (print_reminder) {
698     ua->InfoMsg(_("Do not forget to mount the drive!!!\n"));
699   }
700 
701   /*
702    * close socket opened by native_send_label_request()
703    */
704   CloseSdBsock(ua);
705 
706   return 1;
707 }
708 
709 /*
710  * Label a tape
711  *
712  *   label storage=xxx volume=vvv
713  */
LabelCmd(UaContext * ua,const char * cmd)714 bool LabelCmd(UaContext* ua, const char* cmd)
715 {
716   return do_label(ua, cmd, false); /* standard label */
717 }
718 
RelabelCmd(UaContext * ua,const char * cmd)719 bool RelabelCmd(UaContext* ua, const char* cmd)
720 {
721   return do_label(ua, cmd, true); /* relabel tape */
722 }
723 
724 /*
725  * Check if the Volume name has legal characters
726  * If ua is non-NULL send the message
727  */
IsVolumeNameLegal(UaContext * ua,const char * name)728 bool IsVolumeNameLegal(UaContext* ua, const char* name)
729 {
730   int len;
731   const char* p;
732   const char* accept = ":.-_/";
733 
734   if (name[0] == '/') {
735     if (ua) { ua->ErrorMsg(_("Volume name can not start with \"/\".\n")); }
736     return 0;
737   }
738 
739   /*
740    * Restrict the characters permitted in the Volume name
741    */
742   for (p = name; *p; p++) {
743     if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
744       continue;
745     }
746     if (ua) {
747       ua->ErrorMsg(_("Illegal character \"%c\" in a volume name.\n"), *p);
748     }
749     return 0;
750   }
751   len = strlen(name);
752   if (len >= MAX_NAME_LENGTH) {
753     if (ua) { ua->ErrorMsg(_("Volume name too long.\n")); }
754     return 0;
755   }
756   if (len == 0) {
757     if (ua) {
758       ua->ErrorMsg(_("Volume name must be at least one character long.\n"));
759     }
760     return 0;
761   }
762   return 1;
763 }
764 } /* namespace directordaemon */
765