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