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