1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2011-2012 Planets Communications B.V.
5    Copyright (C) 2013-2018 Bareos GmbH & Co. KG
6 
7    This program is Free Software; you can redistribute it and/or
8    modify it under the terms of version three of the GNU Affero General Public
9    License as published by the Free Software Foundation and included
10    in the file LICENSE.
11 
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15    Affero General Public License for more details.
16 
17    You should have received a copy of the GNU Affero General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301, USA.
21 */
22 /*
23  * Written by Marco van Wieringen, December 2011
24  */
25 /**
26  * @file
27  * BAREOS Director -- Import/Export and Move functions.
28  */
29 
30 #include "include/bareos.h"
31 #include "dird.h"
32 #include "dird/sd_cmds.h"
33 #include "dird/storage.h"
34 #include "dird/ua_label.h"
35 #include "dird/ua_impexp.h"
36 #include "dird/ua_update.h"
37 #include "dird/ua_select.h"
38 #include "lib/edit.h"
39 
40 namespace directordaemon {
41 
42 /* Forward referenced functions */
43 
44 /**
45  * Import/Export and Move volumes in an autochanger.
46  *
47  * The following things apply here:
48  * - the source and destination slot list is walked in order
49  *   So when you give a selection of 1-3,7,5 it will visit the
50  *   slots 1,2,3,5,7 in that order.
51  * - moving volumes from a source slot specification to a
52  *   destination slot specification also is performed in order.
53  *   So when you specify the source slots as 1-3,7,5 and
54  *   the destination slots as 22-24,25,27 the following moves
55  *   will take place:
56  *      1 --> 22
57  *      2 --> 23
58  *      3 --> 24
59  *      5 --> 25
60  *      7 --> 27
61  *
62  * When you want to be sure the moves are performed in the
63  * way you expect them to happen make sure the selection
64  * cannot be wrongly interpreted by the code e.g. use
65  * unambigious ranges. Or ranges of only one slot for
66  * both the source and destination.
67  */
68 
69 /**
70  * Walk the slot list and count the number of slots enabled in
71  * the list.
72  */
count_enabled_slots(char * slot_list,slot_number_t max_slots)73 static inline slot_number_t count_enabled_slots(char* slot_list,
74                                                 slot_number_t max_slots)
75 {
76   slot_number_t i;
77   slot_number_t cnt = 0;
78 
79   for (i = 0; i < max_slots; i++) {
80     if (BitIsSet(i, slot_list)) { cnt++; }
81   }
82   return cnt;
83 }
84 
85 /**
86  * See if a selected slot list has the wanted status and
87  * deselect any slot which has not.
88  */
validate_slot_list(UaContext * ua,StorageResource * store,changer_vol_list_t * vol_list,char * slot_list,slot_status_t status)89 static inline void validate_slot_list(UaContext* ua,
90                                       StorageResource* store,
91                                       changer_vol_list_t* vol_list,
92                                       char* slot_list,
93                                       slot_status_t status)
94 {
95   vol_list_t* vl;
96 
97   /*
98    * Walk the list of drives and slots available.
99    */
100   foreach_dlist (vl, vol_list->contents) {
101     switch (vl->slot_type) {
102       case slot_type_t::kSlotTypeStorage:
103       case slot_type_t::kSlotTypeImport:
104         if (BitIsSet(vl->bareos_slot_number - 1, slot_list)) {
105           switch (status) {
106             case slot_status_t::kSlotStatusFull:
107               /*
108                * If it has the correct status we are done.
109                */
110               if (vl->slot_status == status) { continue; }
111               /*
112                * If the request is for a slot with status
113                * we check the actual status. When its empty
114                * but loaded in the drive we just pretend
115                * that it has status. We just unload the drive
116                * on the export move operation.
117                */
118               if (vl->slot_type == slot_type_t::kSlotTypeStorage &&
119                   vl->slot_status == slot_status_t::kSlotStatusEmpty &&
120                   vol_is_loaded_in_drive(store, vol_list,
121                                          vl->bareos_slot_number) != NULL) {
122                 continue;
123               }
124               Dmsg1(100, "Deselecting slot %hd doesn't have wanted status.\n",
125                     vl->bareos_slot_number);
126               ua->WarningMsg(
127                   _("Deselecting slot %hd doesn't have wanted status.\n"),
128                   vl->bareos_slot_number);
129               ClearBit(vl->bareos_slot_number - 1, slot_list);
130               break;
131             case slot_status_t::kSlotStatusEmpty:
132               /*
133                * If it has the correct status we are done.
134                */
135               if (vl->slot_status == status) { continue; }
136               /*
137                * If the slot is empty and this is normal slot
138                * make sure its not loaded in a drive because
139                * then the slot is not really empty.
140                */
141               if (vl->slot_type == slot_type_t::kSlotTypeStorage &&
142                   vl->slot_status == slot_status_t::kSlotStatusEmpty &&
143                   vol_is_loaded_in_drive(store, vol_list,
144                                          vl->bareos_slot_number) == NULL) {
145                 continue;
146               }
147               Dmsg1(100, "Deselecting slot %hd doesn't have wanted status.\n",
148                     vl->bareos_slot_number);
149               ua->WarningMsg(
150                   _("Deselecting slot %hd doesn't have wanted status.\n"),
151                   vl->bareos_slot_number);
152               ClearBit(vl->bareos_slot_number - 1, slot_list);
153               break;
154             default:
155               break;
156           }
157         }
158         break;
159       default:
160         break;
161     }
162   }
163 }
164 
165 /**
166  * See where a certain slot is referenced.
167  * For a drive slot we check the loaded variable
168  * and for all other slots the exact slotnr.
169  * We only check slots which have content.
170  */
find_slot_in_list(changer_vol_list_t * vol_list,slot_number_t slotnr)171 static inline vol_list_t* find_slot_in_list(changer_vol_list_t* vol_list,
172                                             slot_number_t slotnr)
173 {
174   vol_list_t* vl;
175 
176   foreach_dlist (vl, vol_list->contents) {
177     switch (vl->slot_status) {
178       case slot_status_t::kSlotStatusFull:
179         switch (vl->slot_type) {
180           case slot_type_t::kSlotTypeDrive:
181             if (vl->currently_loaded_slot_number == slotnr) { return vl; }
182             break;
183           default:
184             if (vl->bareos_slot_number == slotnr) { return vl; }
185             break;
186         }
187         break;
188       default:
189         continue;
190     }
191   }
192   return NULL;
193 }
194 
195 /**
196  * Check if a source and destination slot list overlap.
197  * An overlap is solved when there is a slot enabled
198  * in either the source or destination slot list before
199  * the overlap is detected. e.g. then on a move the volume
200  * is first moved somewhere else before either the source
201  * or destination slot is referenced by the next operation.
202  * Then again it wise not to perform to crazy operations
203  * as we will cancel any crazy-ness as soon as we encounter
204  * it.
205  */
slot_lists_overlap(char * src_slot_list,char * dst_slot_list,slot_number_t max_slots)206 static inline bool slot_lists_overlap(char* src_slot_list,
207                                       char* dst_slot_list,
208                                       slot_number_t max_slots)
209 {
210   slot_number_t i;
211   bool other_slot_enabled = false;
212 
213   for (i = 0; i < max_slots; i++) {
214     /*
215      * See if both the source and destination slot are selected
216      * and there has not been a source or destination slot
217      * which has been selected before.
218      */
219     if (BitIsSet(i, src_slot_list) && BitIsSet(i, dst_slot_list) &&
220         !other_slot_enabled) {
221       Dmsg0(100,
222             "Found slot enabled in either source or destination selection "
223             "which overlap\n");
224       return true;
225     } else {
226       if (BitIsSet(i, src_slot_list) || BitIsSet(i, dst_slot_list)) {
227         Dmsg0(100,
228               "Found slot enabled in either source or destination selection\n");
229         other_slot_enabled = true;
230       }
231     }
232   }
233   Dmsg0(100,
234         "Found no slot enabled in either source or destination selection which "
235         "overlap\n");
236   return false;
237 }
238 
239 /**
240  * Scan all slots that are not empty for the exact volumename
241  * by reading the label of the volume replacing the scanned
242  * barcode when available. When a valid source slot list
243  * is given we only check the slots enabled in that slot list.
244  * We return an updated changer_vol_list_t with the new content
245  * of the autochanger after the scan as that may move some
246  * volumes around. We free the old list and return the new.
247  */
scan_slots_for_volnames(UaContext * ua,StorageResource * store,drive_number_t drive,changer_vol_list_t * vol_list,char * src_slot_list)248 static inline changer_vol_list_t* scan_slots_for_volnames(
249     UaContext* ua,
250     StorageResource* store,
251     drive_number_t drive,
252     changer_vol_list_t* vol_list,
253     char* src_slot_list)
254 {
255   changer_vol_list_t* new_vol_list;
256   vol_list_t vls;
257   vol_list_t *vl1, *vl2;
258 
259   /*
260    * Walk the list of drives and slots available.
261    */
262   foreach_dlist (vl1, vol_list->contents) {
263     switch (vl1->slot_type) {
264       case slot_type_t::kSlotTypeDrive:
265         continue;
266       default:
267         /*
268          * See if a slot list selection was done and
269          * if so only get the content for this slot when
270          * it is selected in the slot list.
271          */
272         if (src_slot_list &&
273             !BitIsSet(vl1->bareos_slot_number - 1, src_slot_list)) {
274           continue;
275         }
276 
277         switch (vl1->slot_status) {
278           case slot_status_t::kSlotStatusFull:
279             if (vl1->VolName) {
280               free(vl1->VolName);
281               vl1->VolName = NULL;
282             }
283             vl1->VolName =
284                 get_volume_name_from_SD(ua, vl1->bareos_slot_number, drive);
285             Dmsg2(100, "Got Vol=%s from SD for Slot=%hd\n", vl1->VolName,
286                   vl1->bareos_slot_number);
287             break;
288           case slot_status_t::kSlotStatusEmpty:
289             /*
290              * See if the slot is empty because the volume is
291              * loaded in a drive.
292              */
293             if (vl1->slot_type == slot_type_t::kSlotTypeStorage &&
294                 (vl2 = vol_is_loaded_in_drive(
295                      store, vol_list, vl1->bareos_slot_number)) != NULL) {
296               if (vl2->VolName) {
297                 free(vl2->VolName);
298                 vl2->VolName = NULL;
299               }
300               vl2->VolName =
301                   get_volume_name_from_SD(ua, vl1->bareos_slot_number, drive);
302               Dmsg2(100, "Got Vol=%s from SD for Slot=%hd\n", vl2->VolName,
303                     vl1->bareos_slot_number);
304             }
305             break;
306           default:
307             continue;
308         }
309         break;
310     }
311   }
312 
313   /*
314    * As the scan for volumes can alter the location of
315    * the volumes in the autochanger e.g. volumes in drives
316    * being put back into slots etc we rescan the changer.
317    */
318   new_vol_list = get_vol_list_from_storage(ua, store, true /* listall */,
319                                            true /* want to see all slots */,
320                                            false /* non cached list */);
321   if (!new_vol_list) {
322     /*
323      * Free the old vol_list and return a NULL vol_list.
324      */
325     StorageFreeVolList(store, vol_list);
326     return NULL;
327   }
328 
329   /*
330    * Walk the list of drives and slots available.
331    * And copy the new scanned volume names from the old list
332    * to the new list.
333    *
334    * This is optimized for the case the slots are still
335    * filled with the same volume.
336    */
337   foreach_dlist (vl1, new_vol_list->contents) {
338     switch (vl1->slot_type) {
339       case slot_type_t::kSlotTypeDrive:
340         switch (vl1->slot_status) {
341           case slot_status_t::kSlotStatusFull:
342             /*
343              * Lookup the drive in the old list.
344              */
345             vls.element_address = vl1->element_address;
346             vl2 = (vol_list_t*)vol_list->contents->binary_search(
347                 (void*)&vls, StorageCompareVolListEntry);
348             if (vl2 && vl2->slot_status == slot_status_t::kSlotStatusFull &&
349                 vl2->currently_loaded_slot_number ==
350                     vl1->currently_loaded_slot_number) {
351               /*
352                * Volume in drive is the same copy the volume name.
353                */
354               if (vl2->VolName) { free(vl2->VolName); }
355               vl2->VolName = vl1->VolName;
356               vl1->VolName = NULL;
357             } else {
358               /*
359                * Drive is loaded with a volume which was previously
360                * loaded somewhere else. Lookup the currently loaded
361                * volume in the old list.
362                */
363               vl2 = find_slot_in_list(vol_list,
364                                       vl1->currently_loaded_slot_number);
365               if (vl2) {
366                 if (vl2->VolName) { free(vl2->VolName); }
367                 vl2->VolName = vl1->VolName;
368                 vl1->VolName = NULL;
369               }
370             }
371             break;
372           default:
373             continue;
374         }
375         break;
376       case slot_type_t::kSlotTypeStorage:
377       case slot_type_t::kSlotTypeImport:
378         /*
379          * See if a slot list selection was done and
380          * if so only get the content for this slot when
381          * it is selected in the slot list.
382          */
383         if (src_slot_list &&
384             !BitIsSet(vl1->bareos_slot_number - 1, src_slot_list)) {
385           continue;
386         }
387         switch (vl1->slot_status) {
388           case slot_status_t::kSlotStatusFull:
389             /*
390              * Lookup the slot in the old list.
391              */
392             vls.element_address = vl1->element_address;
393             vl2 = (vol_list_t*)vol_list->contents->binary_search(
394                 (void*)&vls, StorageCompareVolListEntry);
395             if (vl2 && vl2->slot_status == slot_status_t::kSlotStatusFull &&
396                 vl2->bareos_slot_number == vl1->bareos_slot_number) {
397               /*
398                * Volume in slot is the same copy the volume name.
399                */
400               if (vl2->VolName) { free(vl2->VolName); }
401               vl2->VolName = vl1->VolName;
402               vl1->VolName = NULL;
403             } else {
404               /*
405                * This should never happen as a volume is always put back
406                * into the same slot it was taken from. But as we have the
407                * code to lookup the old place we take a shot at it.
408                */
409               vl2 = find_slot_in_list(vol_list, vl1->bareos_slot_number);
410               if (vl2) {
411                 if (vl2->VolName) { free(vl2->VolName); }
412                 vl2->VolName = vl1->VolName;
413                 vl1->VolName = NULL;
414               }
415             }
416             break;
417           default:
418             continue;
419         }
420         break;
421       default:
422         break;
423     }
424   }
425 
426   /*
427    * Free the old vol_list and return the new data.
428    */
429   StorageFreeVolList(store, vol_list);
430   return new_vol_list;
431 }
432 
433 /**
434  * Convert a volume name into a slot selection.
435  */
get_slot_list_using_volname(UaContext * ua,StorageResource * store,const char * volumename,changer_vol_list_t * vol_list,char * wanted_slot_list,char * selected_slot_list,slot_number_t max_slots)436 static inline bool get_slot_list_using_volname(UaContext* ua,
437                                                StorageResource* store,
438                                                const char* volumename,
439                                                changer_vol_list_t* vol_list,
440                                                char* wanted_slot_list,
441                                                char* selected_slot_list,
442                                                slot_number_t max_slots)
443 {
444   vol_list_t *vl1, *vl2;
445   bool found = false;
446 
447   if (IsNameValid(volumename)) {
448     foreach_dlist (vl1, vol_list->contents) {
449       /*
450        * We only select normal and import/export slots.
451        */
452       switch (vl1->slot_type) {
453         case slot_type_t::kSlotTypeStorage:
454         case slot_type_t::kSlotTypeImport:
455           /*
456            * When the source slot list is limited we check to
457            * see if this slot should be taken into consideration.
458            */
459           if (wanted_slot_list &&
460               !BitIsSet(vl1->bareos_slot_number - 1, wanted_slot_list)) {
461             continue;
462           }
463 
464           switch (vl1->slot_status) {
465             case slot_status_t::kSlotStatusFull:
466               /*
467                * See if the wanted volume is loaded in this slot.
468                */
469               Dmsg3(
470                   100,
471                   "Checking for volume name in slot %hd, wanted %s, found %s\n",
472                   vl1->bareos_slot_number, volumename,
473                   (vl1->VolName) ? vl1->VolName : "NULL");
474               if (vl1->VolName && bstrcmp(vl1->VolName, volumename)) {
475                 found = true;
476               }
477               break;
478             case slot_status_t::kSlotStatusEmpty:
479               /*
480                * See if this slot is loaded in drive and drive contains wanted
481                * volume
482                */
483               vl2 = vol_is_loaded_in_drive(store, vol_list,
484                                            vl1->bareos_slot_number);
485               if (vl2 != NULL) {
486                 Dmsg3(100,
487                       "Checking for volume name in drive %hd, wanted %s, found "
488                       "%s\n",
489                       vl2->bareos_slot_number, volumename,
490                       (vl2->VolName) ? vl2->VolName : "NULL");
491                 if (vl2->VolName && bstrcmp(vl2->VolName, volumename)) {
492                   found = true;
493                 }
494               } else {
495                 Dmsg1(100, "Skipping empty slot %hd\n",
496                       vl1->bareos_slot_number);
497               }
498               break;
499             default:
500               break;
501           }
502           break;
503         default:
504           break;
505       }
506 
507       /*
508        * If we found a match break the loop.
509        */
510       if (found) { break; }
511     }
512 
513     /*
514      * See if we found the wanted volumename in the list
515      * of available slots in the autochanger and mark the
516      * slot in the slot_list or give a warning when the
517      * volumename was not found.
518      */
519     if (found) {
520       SetBit(vl1->bareos_slot_number - 1, selected_slot_list);
521     } else {
522       Dmsg1(100, "No volume named %s in changer or in selected source slots.\n",
523             volumename);
524       ua->WarningMsg(
525           _("No volume named %s in changer or in selected source slots.\n"),
526           volumename);
527     }
528   } else {
529     Dmsg1(100, "Skipping illegal volumename %s.\n", volumename);
530     ua->WarningMsg(_("Skipping illegal volumename %s.\n"), volumename);
531   }
532 
533   return found;
534 }
535 
536 /**
537  * Convert a number of volume names into a slot selection.
538  */
get_slot_list_using_volnames(UaContext * ua,StorageResource * store,int arg,changer_vol_list_t * vol_list,char * wanted_slot_list,char * selected_slot_list,slot_number_t max_slots)539 static inline slot_number_t get_slot_list_using_volnames(
540     UaContext* ua,
541     StorageResource* store,
542     int arg,
543     changer_vol_list_t* vol_list,
544     char* wanted_slot_list,
545     char* selected_slot_list,
546     slot_number_t max_slots)
547 {
548   slot_number_t i;
549   slot_number_t cnt = 0;
550   char *s, *token, *sep;
551 
552   /*
553    * The arg argument contains the index of the first occurence
554    * of the volume keyword. We scan the whole cmdline for one
555    * or more volume= cmdline parameters.
556    */
557   for (i = arg; i < ua->argc; i++) {
558     if (Bstrcasecmp(ua->argk[i], "volume")) {
559       /*
560        * Parse a volumelist e.g. vol1|vol2 and a single volume e.g. vol1
561        */
562       s = strdup(ua->argv[i]);
563       token = s;
564 
565       /*
566        * We could use strtok() here. But we're not going to, because:
567        * (a) strtok() is deprecated, having been replaced by strsep();
568        * (b) strtok() is broken in significant ways.
569        * we could use strsep() instead, but it's not universally available.
570        * so we grow our own using strchr().
571        */
572       sep = strchr(token, '|');
573       while (sep != NULL) {
574         *sep = '\0';
575         if (get_slot_list_using_volname(ua, store, token, vol_list,
576                                         wanted_slot_list, selected_slot_list,
577                                         max_slots)) {
578           cnt++;
579         }
580         token = ++sep;
581         sep = strchr(token, '|');
582       }
583 
584       /*
585        * Pick up the last token.
586        */
587       if (*token) {
588         if (get_slot_list_using_volname(ua, store, token, vol_list,
589                                         wanted_slot_list, selected_slot_list,
590                                         max_slots)) {
591           cnt++;
592         }
593       }
594 
595       free(s);
596     }
597   }
598   return cnt;
599 }
600 
601 /**
602  * Create a slot list selection based on the slot type
603  * and slot content. All slots which have the wanted
604  * slot type and wanted slot content are selected.
605  */
auto_fill_slot_selection(StorageResource * store,changer_vol_list_t * vol_list,char * slot_list,slot_type_t type,slot_status_t content)606 static inline slot_number_t auto_fill_slot_selection(
607     StorageResource* store,
608     changer_vol_list_t* vol_list,
609     char* slot_list,
610     slot_type_t type,
611     slot_status_t content)
612 {
613   slot_number_t cnt = 0;
614   vol_list_t* vl;
615 
616   /*
617    * Walk the list of drives and slots available.
618    */
619   foreach_dlist (vl, vol_list->contents) {
620     /*
621      * Make sure slot_type and slot_status match.
622      */
623     if (vl->slot_type != type || vl->slot_status != content) {
624       Dmsg3(100, "Skipping slot %hd, Type %hd, Content %hd\n",
625             vl->bareos_slot_number, vl->slot_type, vl->slot_status);
626       continue;
627     }
628 
629     /*
630      * If the slot is empty and this is normal slot
631      * make sure its not loaded in a drive because
632      * then the slot is not really empty.
633      */
634     if (type == slot_type_t::kSlotTypeStorage &&
635         content == slot_status_t::kSlotStatusEmpty &&
636         vol_is_loaded_in_drive(store, vol_list, vl->bareos_slot_number) !=
637             NULL) {
638       Dmsg3(100,
639             "Skipping slot %hd, Type %hd, Content %hd is empty but loaded in "
640             "drive\n",
641             vl->bareos_slot_number, vl->slot_type, vl->slot_status);
642       continue;
643     }
644 
645     /*
646      * Mark the slot as selected in the slot list.
647      * And increase the number of slots selected.
648      */
649     Dmsg3(100,
650           "Selected slot %hd which has slot_type %hd and content_type %hd\n",
651           vl->bareos_slot_number, vl->slot_type, vl->slot_status);
652     SetBit(vl->bareos_slot_number - 1, slot_list);
653     cnt++;
654   }
655   return cnt;
656 }
657 
658 /**
659  * Verify if all slots in the given slot list are of a certain
660  * type and have a given content.
661  */
verify_slot_list(StorageResource * store,changer_vol_list_t * vol_list,char * slot_list,slot_type_t type,slot_status_t content)662 static inline bool verify_slot_list(StorageResource* store,
663                                     changer_vol_list_t* vol_list,
664                                     char* slot_list,
665                                     slot_type_t type,
666                                     slot_status_t content)
667 {
668   vol_list_t* vl;
669 
670   /*
671    * Walk the list of drives and slots available.
672    */
673   foreach_dlist (vl, vol_list->contents) {
674     /*
675      * Move operations are only allowed between
676      * normal slots and import/export slots so
677      * don't consider any other slot type.
678      */
679     switch (vl->slot_type) {
680       case slot_type_t::kSlotTypeStorage:
681       case slot_type_t::kSlotTypeImport:
682         if (BitIsSet(vl->bareos_slot_number - 1, slot_list)) {
683           /*
684            * If the type and content is ok we can continue with the next one.
685            */
686           if (vl->slot_type == type && vl->slot_status == content) { continue; }
687 
688           /*
689            * When the content is not the wanted and this is a normal
690            * slot take into consideration if its loaded into the drive.
691            * When we are asked for an empty slot it should NOT be loaded
692            * in the drive but when we are asked for a full slot it being
693            * loaded in the drive also makes that the slot is filled as
694            * we can just release the drive so that its put back into
695            * the slot and then moved.
696            */
697           if (vl->slot_type == slot_type_t::kSlotTypeStorage) {
698             switch (content) {
699               case slot_status_t::kSlotStatusEmpty:
700                 if (vol_is_loaded_in_drive(store, vol_list,
701                                            vl->bareos_slot_number) != NULL) {
702                   Dmsg3(100,
703                         "Skipping slot %hd, Type %hd, Content %hd is empty but "
704                         "loaded in drive\n",
705                         vl->bareos_slot_number, vl->slot_type, vl->slot_status);
706                   return false;
707                 }
708                 break;
709               case slot_status_t::kSlotStatusFull:
710                 if (vol_is_loaded_in_drive(store, vol_list,
711                                            vl->bareos_slot_number) != NULL) {
712                   continue;
713                 }
714                 break;
715               default:
716                 break;
717             }
718           }
719 
720           /*
721            * Not the wanted type or content and not a special case.
722            */
723           Dmsg3(100, "Skipping slot %hd, Type %hd, Content %hd\n",
724                 vl->bareos_slot_number, vl->slot_type, vl->slot_status);
725           return false;
726         }
727         break;
728       default:
729         break;
730     }
731   }
732   return true;
733 }
734 
735 /**
736  * Perform an internal update of our view of the autochanger on a move
737  * instruction without requesting the new status from the SD again.
738  */
update_internal_slot_list(changer_vol_list_t * vol_list,slot_number_t source,slot_number_t destination)739 static inline bool update_internal_slot_list(changer_vol_list_t* vol_list,
740                                              slot_number_t source,
741                                              slot_number_t destination)
742 {
743   bool found;
744   vol_list_t *vl1, *vl2;
745 
746   /*
747    * First lookup the source and destination slots in the vol_list.
748    */
749   found = false;
750   foreach_dlist (vl1, vol_list->contents) {
751     switch (vl1->slot_type) {
752       case slot_type_t::kSlotTypeDrive:
753         continue;
754       default:
755         if (vl1->bareos_slot_number == source) { found = true; }
756         break;
757     }
758     if (found) { break; }
759   }
760 
761   found = false;
762   foreach_dlist (vl2, vol_list->contents) {
763     switch (vl2->slot_type) {
764       case slot_type_t::kSlotTypeDrive:
765         continue;
766       default:
767         if (vl2->bareos_slot_number == destination) { found = true; }
768         break;
769     }
770     if (found) { break; }
771   }
772 
773   if (vl1 && vl2) {
774     /*
775      * Swap the data.
776      */
777     vl2->VolName = vl1->VolName;
778     vl2->slot_status = slot_status_t::kSlotStatusFull;
779     vl1->VolName = NULL;
780     vl1->slot_status = slot_status_t::kSlotStatusEmpty;
781     Dmsg5(100,
782           "Update internal slotlist slot %hd with volname %s, content %hd and "
783           "slot %hd with content %hd and volname NULL\n",
784           vl2->bareos_slot_number, (vl2->VolName) ? vl2->VolName : "NULL",
785           vl2->slot_status, vl1->bareos_slot_number, vl1->slot_status);
786     return true;
787   }
788   return false;
789 }
790 
791 /**
792  * Unload a volume currently loaded in a drive.
793  */
release_loaded_volume(UaContext * ua,StorageResource * store,drive_number_t drive,changer_vol_list_t * vol_list)794 static bool release_loaded_volume(UaContext* ua,
795                                   StorageResource* store,
796                                   drive_number_t drive,
797                                   changer_vol_list_t* vol_list)
798 {
799   bool found;
800   vol_list_t *vl1, *vl2;
801 
802   if (!DoAutochangerVolumeOperation(ua, store, "release", drive, -1)) {
803     return false;
804   }
805 
806   /*
807    * Lookup the drive in the vol_list.
808    */
809   found = false;
810   foreach_dlist (vl1, vol_list->contents) {
811     switch (vl1->slot_type) {
812       case slot_type_t::kSlotTypeDrive:
813         if (vl1->bareos_slot_number == drive) { found = true; }
814         break;
815       default:
816         break;
817     }
818     /*
819      * As drives are at the front of the list
820      * when we see the first non drive we are done.
821      */
822     if (found || vl1->slot_type != slot_type_t::kSlotTypeDrive) { break; }
823   }
824 
825   /*
826    * Lookup the slot in the slotlist referenced by the loaded value in the drive
827    * slot.
828    */
829   found = false;
830   foreach_dlist (vl2, vol_list->contents) {
831     switch (vl2->slot_type) {
832       case slot_type_t::kSlotTypeDrive:
833         continue;
834       default:
835         if (vl2->bareos_slot_number == vl1->currently_loaded_slot_number) {
836           found = true;
837         }
838         break;
839     }
840     if (found) { break; }
841   }
842 
843   if (vl1 && vl2) {
844     /*
845      * Swap the data.
846      */
847     vl2->VolName = vl1->VolName;
848     vl2->slot_status = slot_status_t::kSlotStatusFull;
849     vl1->VolName = NULL;
850     vl1->slot_status = slot_status_t::kSlotStatusEmpty;
851     vl1->currently_loaded_slot_number = 0;
852     Dmsg5(100,
853           "Update internal slotlist slot %hd with volname %s, content %hd and "
854           "slot %hd with content %hd and volname NULL\n",
855           vl2->bareos_slot_number, (vl2->VolName) ? vl2->VolName : "NULL",
856           vl2->slot_status, vl1->bareos_slot_number, vl1->slot_status);
857     return true;
858   }
859   return false;
860 }
861 
862 /**
863  * Ask the autochanger to move volume from a source slot
864  * to a destination slot by walking the two filled
865  * slot lists and marking every visited slot.
866  */
move_volumes_in_autochanger(UaContext * ua,enum e_move_op operation,StorageResource * store,changer_vol_list_t * vol_list,char * src_slot_list,char * dst_slot_list,slot_number_t max_slots)867 static char* move_volumes_in_autochanger(UaContext* ua,
868                                          enum e_move_op operation,
869                                          StorageResource* store,
870                                          changer_vol_list_t* vol_list,
871                                          char* src_slot_list,
872                                          char* dst_slot_list,
873                                          slot_number_t max_slots)
874 {
875   vol_list_t* vl;
876   slot_number_t transfer_from, transfer_to;
877   char* visited_slot_list;
878   slot_number_t nr_enabled_src_slots, nr_enabled_dst_slots;
879 
880   /*
881    * Sanity check.
882    */
883   nr_enabled_src_slots = count_enabled_slots(src_slot_list, max_slots);
884   nr_enabled_dst_slots = count_enabled_slots(dst_slot_list, max_slots);
885   if (nr_enabled_src_slots == 0 || nr_enabled_dst_slots == 0) {
886     ua->WarningMsg(_("Nothing to do\n"));
887     return NULL;
888   }
889 
890   /*
891    * When doing an export we first set all slots in the database
892    * to inchanger = 0 so we cannot get surprises that a running
893    * backup can grab such a volume.
894    */
895   switch (operation) {
896     case VOLUME_EXPORT:
897       UpdateInchangerForExport(ua, store, vol_list, src_slot_list);
898       break;
899     default:
900       break;
901   }
902 
903   /*
904    * Create an empty slot list in which we keep track of the slots
905    * we visited during this move operation so we can return that data
906    * to the caller which can use it to update only the slots updated
907    * by the move operation.
908    */
909   visited_slot_list = (char*)malloc(NbytesForBits(max_slots));
910   ClearAllBits(max_slots, visited_slot_list);
911 
912   transfer_to = 1;
913   for (transfer_from = 1; transfer_from <= max_slots; transfer_from++) {
914     /*
915      * See if the slot is marked in the source slot list.
916      */
917     if (BitIsSet(transfer_from - 1, src_slot_list)) {
918       /*
919        * Search for the first marked slot in the destination selection.
920        */
921       while (transfer_to <= max_slots) {
922         if (BitIsSet(transfer_to - 1, dst_slot_list)) { break; }
923         transfer_to++;
924       }
925 
926       /*
927        * This should never happen but a sanity check just in case.
928        */
929       if (transfer_to > max_slots) {
930         Dmsg0(100, "Failed to find suitable destination slot in slot range.\n");
931         ua->WarningMsg(
932             _("Failed to find suitable destination slot in slot range.\n"));
933         break;
934       }
935 
936       /*
937        * Based on the operation see if we need to unload the drive.
938        */
939       switch (operation) {
940         case VOLUME_EXPORT:
941           /*
942            * Sanity check to see if the volume being exported is in the drive.
943            * If so we release the drive and then perform the actual move.
944            */
945           vl = vol_is_loaded_in_drive(store, vol_list, transfer_from);
946           if (vl != NULL) {
947             if (!release_loaded_volume(ua, store, vl->bareos_slot_number,
948                                        vol_list)) {
949               Dmsg1(100, "Failed to release volume in drive %hd\n",
950                     vl->bareos_slot_number);
951               ua->WarningMsg(_("Failed to release volume in drive %hd\n"),
952                              vl->bareos_slot_number);
953               continue;
954             }
955           }
956           break;
957         default:
958           break;
959       }
960 
961       /*
962        * If we found a source and destination slot perform the move.
963        */
964       if (transfer_volume(ua, store, transfer_from, transfer_to)) {
965         Dmsg2(100,
966               "Successfully moved volume from source slot %hd to destination "
967               "slot %hd\n",
968               transfer_from, transfer_to);
969         update_internal_slot_list(vol_list, transfer_from, transfer_to);
970         SetBit(transfer_from - 1, visited_slot_list);
971         SetBit(transfer_to - 1, visited_slot_list);
972       } else {
973         Dmsg2(100,
974               "Failed to move volume from source slot %hd to destination slot "
975               "%hd\n",
976               transfer_from, transfer_to);
977         ua->WarningMsg(_("Failed to move volume from source slot %hd to "
978                          "destination slot %hd\n"),
979                        transfer_from, transfer_to);
980         switch (operation) {
981           case VOLUME_EXPORT:
982             /*
983              * For an export we always set the source slot as visited so when
984              * the move operation fails we update the inchanger flag in the
985              * database back to 1 so we know it still is in the changer.
986              */
987             SetBit(transfer_from - 1, visited_slot_list);
988             break;
989           default:
990             break;
991         }
992       }
993       transfer_to++;
994     }
995   }
996 
997   return visited_slot_list;
998 }
999 
1000 /**
1001  * Perform the actual move operation which is either a:
1002  * - import of import slots into normal slots
1003  * - export of normal slots into export slots
1004  * - move from one normal slot to another normal slot
1005  */
PerformMoveOperation(UaContext * ua,enum e_move_op operation)1006 static bool PerformMoveOperation(UaContext* ua, enum e_move_op operation)
1007 {
1008   bool scan;
1009   UnifiedStorageResource store;
1010   changer_vol_list_t* vol_list;
1011   char *src_slot_list = NULL, *dst_slot_list = NULL, *tmp_slot_list = NULL,
1012        *visited_slot_list = NULL;
1013   slot_number_t nr_enabled_src_slots = 0, nr_enabled_dst_slots = 0;
1014   drive_number_t drive = kInvalidDriveNumber;
1015   slot_number_t max_slots;
1016   int list_index;
1017   bool retval = false;
1018 
1019   store.store = get_storage_resource(ua, false, true);
1020   if (!store.store) { return retval; }
1021 
1022   PmStrcpy(store.store_source, _("command line"));
1023   SetWstorage(ua->jcr, &store);
1024 
1025   /*
1026    * See if the scan option was given.
1027    * We need a drive for the scanning so ask if
1028    * the scan option was specified.
1029    */
1030   scan = FindArg(ua, NT_("scan")) >= 0;
1031   if (scan) { drive = GetStorageDrive(ua, store.store); }
1032 
1033   /*
1034    * Get the number of slots in the autochanger for
1035    * sizing the slot lists.
1036    */
1037   max_slots = GetNumSlots(ua, store.store);
1038   if (max_slots <= 0) {
1039     ua->WarningMsg(_("No slots in changer.\n"));
1040     return retval;
1041   }
1042 
1043   /*
1044    * Get the current content of the autochanger for
1045    * validation and selection purposes.
1046    */
1047   vol_list = get_vol_list_from_storage(ua, store.store, true /* listall */,
1048                                        true /* want to see all slots */);
1049   if (!vol_list) {
1050     ua->WarningMsg(_("No Volumes found, or no barcodes.\n"));
1051     goto bail_out;
1052   }
1053 
1054   /*
1055    * See if there are any source slot selections.
1056    */
1057   list_index = FindArgWithValue(ua, "srcslots");
1058   if (list_index < 0) { list_index = FindArgWithValue(ua, "srcslot"); }
1059   if (list_index >= 0) {
1060     src_slot_list = (char*)malloc(NbytesForBits(max_slots));
1061     ClearAllBits(max_slots, src_slot_list);
1062     if (!GetUserSlotList(ua, src_slot_list, "srcslots", max_slots)) {
1063       goto bail_out;
1064     } else {
1065       /*
1066        * See if we should scan slots for the correct
1067        * volume name or that we can use the barcodes.
1068        * If a set of src slots was given we only scan
1069        * the content of those slots.
1070        */
1071       if (scan) {
1072         vol_list = scan_slots_for_volnames(ua, store.store, drive, vol_list,
1073                                            src_slot_list);
1074         if (!vol_list) { goto bail_out; }
1075       }
1076 
1077       /*
1078        * Clear any slot that has no content in the source selection.
1079        */
1080       validate_slot_list(ua, store.store, vol_list, src_slot_list,
1081                          slot_status_t::kSlotStatusFull);
1082       nr_enabled_src_slots = count_enabled_slots(src_slot_list, max_slots);
1083     }
1084   } else {
1085     /*
1086      * See if we should scan slots for the correct
1087      * volume name or that we can use the barcodes.
1088      */
1089     if (scan) {
1090       vol_list = scan_slots_for_volnames(ua, store.store, drive, vol_list,
1091                                          src_slot_list);
1092       if (!vol_list) { goto bail_out; }
1093     }
1094   }
1095 
1096   /*
1097    * See if there are any destination slot selections.
1098    */
1099   list_index = FindArgWithValue(ua, "dstslots");
1100   if (list_index < 0) { list_index = FindArgWithValue(ua, "dstslot"); }
1101   if (list_index >= 0) {
1102     dst_slot_list = (char*)malloc(NbytesForBits(max_slots));
1103     ClearAllBits(max_slots, dst_slot_list);
1104     if (!GetUserSlotList(ua, dst_slot_list, "dstslots", max_slots)) {
1105       goto bail_out;
1106     } else {
1107       /*
1108        * Clear any slot in the destination slot list which has not the wanted
1109        * content.
1110        */
1111       switch (operation) {
1112         case VOLUME_IMPORT:
1113         case VOLUME_EXPORT:
1114           validate_slot_list(ua, store.store, vol_list, dst_slot_list,
1115                              slot_status_t::kSlotStatusEmpty);
1116           break;
1117         default:
1118           break;
1119       }
1120       nr_enabled_dst_slots = count_enabled_slots(dst_slot_list, max_slots);
1121     }
1122   }
1123 
1124   /*
1125    * For Import and Export operations we can also use a list
1126    * of volume names for which we lookup the slot they are
1127    * loaded in.
1128    */
1129   switch (operation) {
1130     case VOLUME_IMPORT:
1131     case VOLUME_EXPORT:
1132       list_index = FindArgWithValue(ua, "volume");
1133       if (list_index > 0) {
1134         /*
1135          * Create a new temporary slot list with gets filled
1136          * by the selection criteria on the cmdline. We provide
1137          * the current src_slot_list as an extra selection criteria.
1138          */
1139         tmp_slot_list = (char*)malloc(NbytesForBits(max_slots));
1140         ClearAllBits(max_slots, tmp_slot_list);
1141         nr_enabled_src_slots = get_slot_list_using_volnames(
1142             ua, store.store, list_index, vol_list, src_slot_list, tmp_slot_list,
1143             max_slots);
1144         if (src_slot_list) { free(src_slot_list); }
1145         src_slot_list = tmp_slot_list;
1146       }
1147       break;
1148     default:
1149       break;
1150   }
1151 
1152   Dmsg2(100, "src_slots = %hd, dst_slots = %hd\n", nr_enabled_src_slots,
1153         nr_enabled_dst_slots);
1154 
1155   /*
1156    * First generic sanity check if there is a source selection the number
1157    * of selected slots in the source must be less or equal to the
1158    * number of slots in the destination
1159    */
1160   if (nr_enabled_src_slots && nr_enabled_dst_slots &&
1161       nr_enabled_src_slots > nr_enabled_dst_slots) {
1162     ua->WarningMsg(
1163         _("Source slot selection doesn't fit into destination slot "
1164           "selection.\n"));
1165     goto bail_out;
1166   }
1167 
1168   /*
1169    * Detect any conflicting overlaps in source and destination selection.
1170    */
1171   if (nr_enabled_src_slots && nr_enabled_dst_slots &&
1172       slot_lists_overlap(src_slot_list, dst_slot_list, max_slots)) {
1173     ua->WarningMsg(
1174         _("Source slot selection and destination slot selection overlap.\n"));
1175     goto bail_out;
1176   }
1177 
1178   /*
1179    * Operation specific checks.
1180    */
1181   switch (operation) {
1182     case VOLUME_EXPORT:
1183       if (nr_enabled_src_slots == 0) {
1184         ua->WarningMsg(
1185             _("Cannot perform an export operation without source slot "
1186               "selection\n"));
1187         goto bail_out;
1188       }
1189       break;
1190     case VOLUME_MOVE:
1191       if (nr_enabled_src_slots == 0 || nr_enabled_dst_slots == 0) {
1192         ua->WarningMsg(
1193             _("Cannot perform a move operation without source and/or "
1194               "destination selection\n"));
1195         goto bail_out;
1196       }
1197       break;
1198     default:
1199       break;
1200   }
1201 
1202   switch (operation) {
1203     case VOLUME_IMPORT:
1204       /*
1205        * Perform an autofill of the source slots when none are selected.
1206        */
1207       if (nr_enabled_src_slots == 0) {
1208         src_slot_list = (char*)malloc(NbytesForBits(max_slots));
1209         ClearAllBits(max_slots, src_slot_list);
1210         nr_enabled_src_slots = auto_fill_slot_selection(
1211             store.store, vol_list, src_slot_list, slot_type_t::kSlotTypeImport,
1212             slot_status_t::kSlotStatusFull);
1213       } else {
1214         /*
1215          * All slots in the source selection need to be import/export slots and
1216          * filled.
1217          */
1218         if (!verify_slot_list(store.store, vol_list, src_slot_list,
1219                               slot_type_t::kSlotTypeImport,
1220                               slot_status_t::kSlotStatusFull)) {
1221           ua->WarningMsg(
1222               _("Not all slots in source selection are import slots and "
1223                 "filled.\n"));
1224           goto bail_out;
1225         }
1226       }
1227       /*
1228        * Perform an autofill of the destination slots when none are selected.
1229        */
1230       if (nr_enabled_dst_slots == 0) {
1231         dst_slot_list = (char*)malloc(NbytesForBits(max_slots));
1232         ClearAllBits(max_slots, dst_slot_list);
1233         nr_enabled_dst_slots = auto_fill_slot_selection(
1234             store.store, vol_list, dst_slot_list, slot_type_t::kSlotTypeStorage,
1235             slot_status_t::kSlotStatusEmpty);
1236         if (nr_enabled_src_slots > nr_enabled_dst_slots) {
1237           ua->WarningMsg(
1238               _("Not enough free slots available to import %hd volumes\n"),
1239               nr_enabled_src_slots);
1240           goto bail_out;
1241         }
1242       } else {
1243         /*
1244          * All slots in the destination selection need to be normal slots and
1245          * empty.
1246          */
1247         if (!verify_slot_list(store.store, vol_list, dst_slot_list,
1248                               slot_type_t::kSlotTypeStorage,
1249                               slot_status_t::kSlotStatusEmpty)) {
1250           ua->WarningMsg(
1251               _("Not all slots in destination selection are normal slots and "
1252                 "empty.\n"));
1253           goto bail_out;
1254         }
1255       }
1256       visited_slot_list =
1257           move_volumes_in_autochanger(ua, operation, store.store, vol_list,
1258                                       src_slot_list, dst_slot_list, max_slots);
1259       break;
1260     case VOLUME_EXPORT:
1261       /*
1262        * All slots in the source selection need to be normal slots.
1263        */
1264       if (!verify_slot_list(store.store, vol_list, src_slot_list,
1265                             slot_type_t::kSlotTypeStorage,
1266                             slot_status_t::kSlotStatusFull)) {
1267         ua->WarningMsg(
1268             _("Not all slots in source selection are normal slots and "
1269               "filled.\n"));
1270         goto bail_out;
1271       }
1272       /*
1273        * Perform an autofill of the destination slots when none are selected.
1274        */
1275       if (nr_enabled_dst_slots == 0) {
1276         dst_slot_list = (char*)malloc(NbytesForBits(max_slots));
1277         ClearAllBits(max_slots, dst_slot_list);
1278         nr_enabled_dst_slots = auto_fill_slot_selection(
1279             store.store, vol_list, dst_slot_list, slot_type_t::kSlotTypeImport,
1280             slot_status_t::kSlotStatusEmpty);
1281         if (nr_enabled_src_slots > nr_enabled_dst_slots) {
1282           ua->WarningMsg(_("Not enough free export slots available to export "
1283                            "%hd volume%s\n"),
1284                          nr_enabled_src_slots,
1285                          nr_enabled_src_slots > 1 ? "s" : "");
1286           goto bail_out;
1287         }
1288       } else {
1289         /*
1290          * All slots in the destination selection need to be import/export slots
1291          * and empty.
1292          */
1293         if (!verify_slot_list(store.store, vol_list, dst_slot_list,
1294                               slot_type_t::kSlotTypeImport,
1295                               slot_status_t::kSlotStatusEmpty)) {
1296           ua->WarningMsg(
1297               _("Not all slots in destination selection are export slots and "
1298                 "empty.\n"));
1299           goto bail_out;
1300         }
1301       }
1302       visited_slot_list =
1303           move_volumes_in_autochanger(ua, operation, store.store, vol_list,
1304                                       src_slot_list, dst_slot_list, max_slots);
1305       break;
1306     case VOLUME_MOVE:
1307       visited_slot_list =
1308           move_volumes_in_autochanger(ua, operation, store.store, vol_list,
1309                                       src_slot_list, dst_slot_list, max_slots);
1310       break;
1311     default:
1312       break;
1313   }
1314 
1315   /*
1316    * If we actually moved some volumes update the database with the
1317    * new info for those slots.
1318    */
1319   if (visited_slot_list &&
1320       count_enabled_slots(visited_slot_list, max_slots) > 0) {
1321     Dmsg0(100, "Updating database with new info for visited slots\n");
1322     UpdateSlotsFromVolList(ua, store.store, vol_list, visited_slot_list);
1323   }
1324 
1325   retval = true;
1326 
1327 bail_out:
1328   CloseSdBsock(ua);
1329 
1330   if (vol_list) { StorageReleaseVolList(store.store, vol_list); }
1331   if (src_slot_list) { free(src_slot_list); }
1332   if (dst_slot_list) { free(dst_slot_list); }
1333   if (visited_slot_list) { free(visited_slot_list); }
1334 
1335   return retval;
1336 }
1337 
1338 /**
1339  * Import volumes from Import/Export Slots into normal Slots.
1340  */
ImportCmd(UaContext * ua,const char * cmd)1341 bool ImportCmd(UaContext* ua, const char* cmd)
1342 {
1343   return PerformMoveOperation(ua, VOLUME_IMPORT);
1344 }
1345 
1346 /**
1347  * Export volumes from normal slots to Import/Export Slots.
1348  */
ExportCmd(UaContext * ua,const char * cmd)1349 bool ExportCmd(UaContext* ua, const char* cmd)
1350 {
1351   return PerformMoveOperation(ua, VOLUME_EXPORT);
1352 }
1353 
1354 /**
1355  * Move volume from one slot to another.
1356  */
move_cmd(UaContext * ua,const char * cmd)1357 bool move_cmd(UaContext* ua, const char* cmd)
1358 {
1359   return PerformMoveOperation(ua, VOLUME_MOVE);
1360 }
1361 } /* namespace directordaemon */
1362