1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2016 Planets Communications B.V.
6 Copyright (C) 2013-2019 Bareos GmbH & Co. KG
7
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
11 in the file LICENSE.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301, USA.
22 */
23 /*
24 * Kern Sibbald, October MM
25 * Extracted from other source files by Marco van Wieringen, February 2016
26 */
27 /**
28 * @file
29 * Storage specific routines.
30 */
31
32 #include "include/bareos.h"
33 #include "dird/dird.h"
34 #include "dird/dird_globals.h"
35 #include "dird/jcr_private.h"
36 #include "dird/sd_cmds.h"
37 #include "include/auth_protocol_types.h"
38 #include "lib/parse_conf.h"
39 #include "lib/util.h"
40
41 #if HAVE_NDMP
42 #include "ndmp/ndmagents.h"
43 #endif
44
45 #include "dird/ndmp_dma_storage.h"
46 #include "dird/storage.h"
47
48 namespace directordaemon {
49
50 /* Forward referenced functions */
51
52 /**
53 * Copy the storage definitions from an alist to the JobControlRecord
54 */
CopyRwstorage(JobControlRecord * jcr,alist * storage,const char * where)55 void CopyRwstorage(JobControlRecord* jcr, alist* storage, const char* where)
56 {
57 if (jcr->JobReads()) { CopyRstorage(jcr, storage, where); }
58 CopyWstorage(jcr, storage, where);
59 }
60
61 /**
62 * Set storage override.
63 * Releases any previous storage definition.
64 */
SetRwstorage(JobControlRecord * jcr,UnifiedStorageResource * store)65 void SetRwstorage(JobControlRecord* jcr, UnifiedStorageResource* store)
66 {
67 if (!store) {
68 Jmsg(jcr, M_FATAL, 0, _("No storage specified.\n"));
69 return;
70 }
71 if (jcr->JobReads()) { SetRstorage(jcr, store); }
72 SetWstorage(jcr, store);
73 }
74
FreeRwstorage(JobControlRecord * jcr)75 void FreeRwstorage(JobControlRecord* jcr)
76 {
77 FreeRstorage(jcr);
78 FreeWstorage(jcr);
79 }
80
81 /**
82 * Copy the storage definitions from an alist to the JobControlRecord
83 */
CopyRstorage(JobControlRecord * jcr,alist * storage,const char * where)84 void CopyRstorage(JobControlRecord* jcr, alist* storage, const char* where)
85 {
86 if (storage) {
87 StorageResource* store = nullptr;
88 if (jcr->impl->res.read_storage_list) {
89 delete jcr->impl->res.read_storage_list;
90 }
91 jcr->impl->res.read_storage_list = new alist(10, not_owned_by_alist);
92 foreach_alist (store, storage) {
93 jcr->impl->res.read_storage_list->append(store);
94 }
95 if (!jcr->impl->res.rstore_source) {
96 jcr->impl->res.rstore_source = GetPoolMemory(PM_MESSAGE);
97 }
98 PmStrcpy(jcr->impl->res.rstore_source, where);
99 if (jcr->impl->res.read_storage_list) {
100 jcr->impl->res.read_storage =
101 (StorageResource*)jcr->impl->res.read_storage_list->first();
102 }
103 }
104 }
105
106 /**
107 * Set storage override.
108 * Remove all previous storage.
109 */
SetRstorage(JobControlRecord * jcr,UnifiedStorageResource * store)110 void SetRstorage(JobControlRecord* jcr, UnifiedStorageResource* store)
111 {
112 StorageResource* storage = nullptr;
113
114 if (!store->store) { return; }
115 if (jcr->impl->res.read_storage_list) { FreeRstorage(jcr); }
116 if (!jcr->impl->res.read_storage_list) {
117 jcr->impl->res.read_storage_list = new alist(10, not_owned_by_alist);
118 }
119 jcr->impl->res.read_storage = store->store;
120 if (!jcr->impl->res.rstore_source) {
121 jcr->impl->res.rstore_source = GetPoolMemory(PM_MESSAGE);
122 }
123 PmStrcpy(jcr->impl->res.rstore_source, store->store_source);
124 foreach_alist (storage, jcr->impl->res.read_storage_list) {
125 if (store->store == storage) { return; }
126 }
127 /* Store not in list, so add it */
128 jcr->impl->res.read_storage_list->prepend(store->store);
129 }
130
FreeRstorage(JobControlRecord * jcr)131 void FreeRstorage(JobControlRecord* jcr)
132 {
133 if (jcr->impl->res.read_storage_list) {
134 delete jcr->impl->res.read_storage_list;
135 jcr->impl->res.read_storage_list = NULL;
136 }
137 jcr->impl->res.read_storage = NULL;
138 }
139
140 /**
141 * Copy the storage definitions from an alist to the JobControlRecord
142 */
CopyWstorage(JobControlRecord * jcr,alist * storage,const char * where)143 void CopyWstorage(JobControlRecord* jcr, alist* storage, const char* where)
144 {
145 if (storage) {
146 StorageResource* st = nullptr;
147 if (jcr->impl->res.write_storage_list) {
148 delete jcr->impl->res.write_storage_list;
149 }
150 jcr->impl->res.write_storage_list = new alist(10, not_owned_by_alist);
151 foreach_alist (st, storage) {
152 Dmsg1(100, "write_storage_list=%s\n", st->resource_name_);
153 jcr->impl->res.write_storage_list->append(st);
154 }
155 if (!jcr->impl->res.wstore_source) {
156 jcr->impl->res.wstore_source = GetPoolMemory(PM_MESSAGE);
157 }
158 PmStrcpy(jcr->impl->res.wstore_source, where);
159 if (jcr->impl->res.write_storage_list) {
160 jcr->impl->res.write_storage =
161 (StorageResource*)jcr->impl->res.write_storage_list->first();
162 Dmsg2(100, "write_storage=%s where=%s\n",
163 jcr->impl->res.write_storage->resource_name_,
164 jcr->impl->res.wstore_source);
165 }
166 }
167 }
168
169 /**
170 * Set storage override.
171 * Remove all previous storage.
172 */
SetWstorage(JobControlRecord * jcr,UnifiedStorageResource * store)173 void SetWstorage(JobControlRecord* jcr, UnifiedStorageResource* store)
174 {
175 StorageResource* storage = nullptr;
176
177 if (!store->store) { return; }
178 if (jcr->impl->res.write_storage_list) { FreeWstorage(jcr); }
179 if (!jcr->impl->res.write_storage_list) {
180 jcr->impl->res.write_storage_list = new alist(10, not_owned_by_alist);
181 }
182 jcr->impl->res.write_storage = store->store;
183 if (!jcr->impl->res.wstore_source) {
184 jcr->impl->res.wstore_source = GetPoolMemory(PM_MESSAGE);
185 }
186 PmStrcpy(jcr->impl->res.wstore_source, store->store_source);
187 Dmsg2(50, "write_storage=%s where=%s\n",
188 jcr->impl->res.write_storage->resource_name_,
189 jcr->impl->res.wstore_source);
190 foreach_alist (storage, jcr->impl->res.write_storage_list) {
191 if (store->store == storage) { return; }
192 }
193
194 /*
195 * Store not in list, so add it
196 */
197 jcr->impl->res.write_storage_list->prepend(store->store);
198 }
199
FreeWstorage(JobControlRecord * jcr)200 void FreeWstorage(JobControlRecord* jcr)
201 {
202 if (jcr->impl->res.write_storage_list) {
203 delete jcr->impl->res.write_storage_list;
204 jcr->impl->res.write_storage_list = NULL;
205 }
206 jcr->impl->res.write_storage = NULL;
207 }
208
209 /**
210 * For NDMP backup we can setup the backup to run to a NDMP instance
211 * of the same Storage Daemon that also does native native backups.
212 * This way a Normal Storage Daemon can perform NDMP protocol based
213 * saves and restores.
214 */
SetPairedStorage(JobControlRecord * jcr)215 void SetPairedStorage(JobControlRecord* jcr)
216 {
217 StorageResource *store = nullptr, *paired_read_write_storage = nullptr;
218
219 switch (jcr->getJobType()) {
220 case JT_BACKUP:
221 /*
222 * For a backup we look at the write storage.
223 */
224 if (jcr->impl->res.write_storage_list) {
225 /*
226 * Setup the jcr->impl_->res.write_storage_list to point to all
227 * paired_storage entries of all the storage currently in the
228 * jcrres.->write_storage_list. Save the original list under
229 * jcr->impl_->res.paired_read_write_storage_list.
230 */
231 jcr->impl->res.paired_read_write_storage_list =
232 jcr->impl->res.write_storage_list;
233 jcr->impl->res.write_storage_list = new alist(10, not_owned_by_alist);
234 foreach_alist (store, jcr->impl->res.paired_read_write_storage_list) {
235 if (store->paired_storage) {
236 Dmsg1(100, "write_storage_list=%s\n",
237 store->paired_storage->resource_name_);
238 jcr->impl->res.write_storage_list->append(store->paired_storage);
239 }
240 }
241
242 /*
243 * Swap the actual jcr->impl_->res.write_storage to point to the paired
244 * storage entry. We save the actual storage entry in
245 * paired_read_write_storage which is for restore in the
246 * FreePairedStorage() function.
247 */
248 store = jcr->impl->res.write_storage;
249 if (store->paired_storage) {
250 jcr->impl->res.write_storage = store->paired_storage;
251 jcr->impl->res.paired_read_write_storage = store;
252 }
253 } else {
254 Jmsg(jcr, M_FATAL, 0,
255 _("No write storage, don't know how to setup paired storage\n"));
256 }
257 break;
258 case JT_RESTORE:
259 /*
260 * For a restores we look at the read storage.
261 */
262 if (jcr->impl->res.read_storage_list) {
263 /*
264 * Setup the jcr->impl_->res.paired_read_write_storage_list to point to
265 * all paired_storage entries of all the storage currently in the
266 * jcr->impl_->res.read_storage_list.
267 */
268 jcr->impl->res.paired_read_write_storage_list =
269 new alist(10, not_owned_by_alist);
270 foreach_alist (paired_read_write_storage,
271 jcr->impl->res.read_storage_list) {
272 store = (StorageResource*)my_config->GetNextRes(R_STORAGE, NULL);
273 while (store) {
274 if (store->paired_storage == paired_read_write_storage) { break; }
275
276 store = (StorageResource*)my_config->GetNextRes(
277 R_STORAGE, (BareosResource*)store);
278 }
279
280 /*
281 * See if we found a store that has the current
282 * paired_read_write_storage as its paired storage.
283 */
284 if (store) {
285 jcr->impl->res.paired_read_write_storage_list->append(store);
286
287 /*
288 * If the current processed paired_read_write_storage is also the
289 * current entry in jcr->impl_->res.read_storage update the
290 * jcr->paired_read_write_storage to point to this storage entry.
291 */
292 if (paired_read_write_storage == jcr->impl->res.read_storage) {
293 jcr->impl->res.paired_read_write_storage = store;
294 }
295 }
296 }
297 } else {
298 Jmsg(jcr, M_FATAL, 0,
299 _("No read storage, don't know how to setup paired storage\n"));
300 }
301 break;
302 case JT_MIGRATE:
303 case JT_COPY:
304 /*
305 * For a migrate or copy we look at the read storage.
306 */
307 if (jcr->impl->res.read_storage_list) {
308 /*
309 * Setup the jcr->impl_->res.read_storage_list to point to all
310 * paired_storage entries of all the storage currently in the
311 * jcr->impl_->res.read_storage_list. Save the original list under
312 * jcr->impl_->res.paired_read_write_storage_list.
313 */
314 jcr->impl->res.paired_read_write_storage_list =
315 jcr->impl->res.read_storage_list;
316 jcr->impl->res.read_storage_list = new alist(10, not_owned_by_alist);
317 foreach_alist (store, jcr->impl->res.paired_read_write_storage_list) {
318 if (store->paired_storage) {
319 Dmsg1(100, "read_storage_list=%s\n",
320 store->paired_storage->resource_name_);
321 jcr->impl->res.read_storage_list->append(store->paired_storage);
322 }
323 }
324
325 /*
326 * Swap the actual jcr->impl_->res.read_storage to point to the paired
327 * storage entry. We save the actual storage entry in
328 * paired_read_write_storage which is for restore in the
329 * FreePairedStorage() function.
330 */
331 store = jcr->impl->res.read_storage;
332 if (store->paired_storage) {
333 jcr->impl->res.read_storage = store->paired_storage;
334 jcr->impl->res.paired_read_write_storage = store;
335 }
336 } else {
337 Jmsg(jcr, M_FATAL, 0,
338 _("No read storage, don't know how to setup paired storage\n"));
339 }
340 break;
341 default:
342 Jmsg(jcr, M_FATAL, 0,
343 _("Unknown Job Type %s, don't know how to setup paired storage\n"),
344 job_type_to_str(jcr->getJobType()));
345 break;
346 }
347 }
348
349 /**
350 * This performs an undo of the actions the SetPairedStorage() function
351 * performed. We reset the storage write storage back to its original
352 * and remove the paired storage override if any.
353 */
FreePairedStorage(JobControlRecord * jcr)354 void FreePairedStorage(JobControlRecord* jcr)
355 {
356 if (jcr->impl->res.paired_read_write_storage_list) {
357 switch (jcr->getJobType()) {
358 case JT_BACKUP:
359 /*
360 * For a backup we look at the write storage.
361 */
362 if (jcr->impl->res.write_storage_list) {
363 /*
364 * The jcr->impl_->res.write_storage_list contain a set of paired
365 * storages. We just delete it content and swap back to the real
366 * master storage.
367 */
368 delete jcr->impl->res.write_storage_list;
369 jcr->impl->res.write_storage_list =
370 jcr->impl->res.paired_read_write_storage_list;
371 jcr->impl->res.paired_read_write_storage_list = NULL;
372 jcr->impl->res.write_storage =
373 jcr->impl->res.paired_read_write_storage;
374 jcr->impl->res.paired_read_write_storage = NULL;
375 }
376 break;
377 case JT_RESTORE:
378 /*
379 * The jcr->impl_->res.read_storage_list contain a set of paired
380 * storages. For the read we created a list of alternative storage which
381 * we can just drop now.
382 */
383 delete jcr->impl->res.paired_read_write_storage_list;
384 jcr->impl->res.paired_read_write_storage_list = NULL;
385 jcr->impl->res.paired_read_write_storage = NULL;
386 break;
387 case JT_MIGRATE:
388 case JT_COPY:
389 /*
390 * For a migrate or copy we look at the read storage.
391 */
392 if (jcr->impl->res.read_storage_list) {
393 /*
394 * The jcr->impl_->res.read_storage_list contains a set of paired
395 * storages. We just delete it content and swap back to the real
396 * master storage.
397 */
398 delete jcr->impl->res.read_storage_list;
399 jcr->impl->res.read_storage_list =
400 jcr->impl->res.paired_read_write_storage_list;
401 jcr->impl->res.paired_read_write_storage_list = NULL;
402 jcr->impl->res.read_storage =
403 jcr->impl->res.paired_read_write_storage;
404 jcr->impl->res.paired_read_write_storage = NULL;
405 }
406 break;
407 default:
408 Jmsg(jcr, M_FATAL, 0,
409 _("Unknown Job Type %s, don't know how to free paired storage\n"),
410 job_type_to_str(jcr->getJobType()));
411 break;
412 }
413 }
414 }
415
416 /**
417 * Check if every possible storage has paired storage associated.
418 */
HasPairedStorage(JobControlRecord * jcr)419 bool HasPairedStorage(JobControlRecord* jcr)
420 {
421 StorageResource* store = nullptr;
422
423 switch (jcr->getJobType()) {
424 case JT_BACKUP:
425 /*
426 * For a backup we look at the write storage.
427 */
428 if (jcr->impl->res.write_storage_list) {
429 foreach_alist (store, jcr->impl->res.write_storage_list) {
430 if (!store->paired_storage) { return false; }
431 }
432 } else {
433 Jmsg(jcr, M_FATAL, 0,
434 _("No write storage, don't know how to check for paired "
435 "storage\n"));
436 return false;
437 }
438 break;
439 case JT_RESTORE:
440 case JT_MIGRATE:
441 case JT_COPY:
442 if (jcr->impl->res.read_storage_list) {
443 foreach_alist (store, jcr->impl->res.read_storage_list) {
444 if (!store->paired_storage) { return false; }
445 }
446 } else {
447 Jmsg(
448 jcr, M_FATAL, 0,
449 _("No read storage, don't know how to check for paired storage\n"));
450 return false;
451 }
452 break;
453 default:
454 Jmsg(jcr, M_FATAL, 0,
455 _("Unknown Job Type %s, don't know how to free paired storage\n"),
456 job_type_to_str(jcr->getJobType()));
457 return false;
458 }
459
460 return true;
461 }
462
463 #define MAX_TRIES 6 * 360 /* 6 hours (10 sec intervals) */
464
465 /**
466 * Change the read storage resource for the current job.
467 */
SelectNextRstore(JobControlRecord * jcr,bootstrap_info & info)468 bool SelectNextRstore(JobControlRecord* jcr, bootstrap_info& info)
469 {
470 UnifiedStorageResource ustore;
471
472 if (bstrcmp(jcr->impl->res.read_storage->resource_name_, info.storage)) {
473 return true; /* Same SD nothing to change */
474 }
475
476 if (!(ustore.store = (StorageResource*)my_config->GetResWithName(
477 R_STORAGE, info.storage))) {
478 Jmsg(jcr, M_FATAL, 0, _("Could not get storage resource '%s'.\n"),
479 info.storage);
480 jcr->setJobStatus(JS_ErrorTerminated);
481 return false;
482 }
483
484 /*
485 * We start communicating with a new storage daemon so close the
486 * old connection when it is still open.
487 */
488 if (jcr->store_bsock) {
489 jcr->store_bsock->close();
490 delete jcr->store_bsock;
491 jcr->store_bsock = NULL;
492 }
493
494 /*
495 * Release current read storage and get a new one
496 */
497 DecReadStore(jcr);
498 FreeRstorage(jcr);
499 SetRstorage(jcr, &ustore);
500 jcr->setJobStatus(JS_WaitSD);
501
502 /*
503 * Wait for up to 6 hours to increment read stoage counter
504 */
505 for (int i = 0; i < MAX_TRIES; i++) {
506 /*
507 * Try to get read storage counter incremented
508 */
509 if (IncReadStore(jcr)) {
510 jcr->setJobStatus(JS_Running);
511 return true;
512 }
513 Bmicrosleep(10, 0); /* Sleep 10 secs */
514 if (JobCanceled(jcr)) {
515 FreeRstorage(jcr);
516 return false;
517 }
518 }
519
520 /*
521 * Failed to IncReadStore()
522 */
523 FreeRstorage(jcr);
524 Jmsg(jcr, M_FATAL, 0, _("Could not acquire read storage lock for \"%s\""),
525 info.storage);
526 return false;
527 }
528
StorageStatus(UaContext * ua,StorageResource * store,char * cmd)529 void StorageStatus(UaContext* ua, StorageResource* store, char* cmd)
530 {
531 switch (store->Protocol) {
532 case APT_NATIVE:
533 return DoNativeStorageStatus(ua, store, cmd);
534 case APT_NDMPV2:
535 case APT_NDMPV3:
536 case APT_NDMPV4:
537 return DoNdmpStorageStatus(ua, store, cmd);
538 default:
539 break;
540 }
541 }
542
543 /**
544 * Simple comparison function for binary insert of vol_list_t
545 */
StorageCompareVolListEntry(void * e1,void * e2)546 int StorageCompareVolListEntry(void* e1, void* e2)
547 {
548 vol_list_t *v1, *v2;
549
550 v1 = (vol_list_t*)e1;
551 v2 = (vol_list_t*)e2;
552
553 ASSERT(v1);
554 ASSERT(v2);
555
556 if (v1->element_address == v2->element_address) {
557 return 0;
558 } else {
559 return (v1->element_address < v2->element_address) ? -1 : 1;
560 }
561 }
562
FreeVolList(changer_vol_list_t * vol_list)563 static inline void FreeVolList(changer_vol_list_t* vol_list)
564 {
565 vol_list_t* vl;
566
567 // make sure cache is treated as empty
568 vol_list->timestamp = 0;
569
570 if (vol_list->contents) {
571 foreach_dlist (vl, vol_list->contents) {
572 if (vl->VolName) { free(vl->VolName); }
573 }
574 vol_list->contents->destroy();
575 delete vol_list->contents;
576 vol_list->contents = NULL;
577 } else {
578 Dmsg0(100, "FreeVolList: vol_list->contents already empty\n");
579 }
580 }
581
582 /**
583 * Generic routine to get the content of a storage autochanger.
584 */
get_vol_list_from_storage(UaContext * ua,StorageResource * store,bool listall,bool scan,bool cached)585 changer_vol_list_t* get_vol_list_from_storage(UaContext* ua,
586 StorageResource* store,
587 bool listall,
588 bool scan,
589 bool cached)
590 {
591 vol_list_type type;
592 dlist* contents = NULL;
593 changer_vol_list_t* vol_list = NULL;
594
595 P(store->runtime_storage_status->changer_lock);
596
597 if (listall) {
598 type = VOL_LIST_ALL;
599 } else {
600 type = VOL_LIST_PARTIAL;
601 }
602
603 /*
604 * Do we have a cached version of the content that is still valid ?
605 */
606 if (store->runtime_storage_status->vol_list) {
607 utime_t now;
608
609 now = (utime_t)time(NULL);
610
611 /*
612 * Are we allowed to return a cached list ?
613 */
614 if (cached && store->runtime_storage_status->vol_list->type == type) {
615 if ((now - store->runtime_storage_status->vol_list->timestamp) <=
616 store->cache_status_interval) {
617 Dmsg0(100, "Using cached storage status\n");
618 vol_list = store->runtime_storage_status->vol_list;
619 vol_list->reference_count++;
620 goto bail_out;
621 }
622 }
623
624 /*
625 * Cached version expired or want non-cached version or wrong type.
626 * Remove the cached contents and retrieve the new contents from the
627 * autochanger.
628 */
629 Dmsg0(100, "Freeing volume list\n");
630 if (store->runtime_storage_status->vol_list->reference_count == 0) {
631 FreeVolList(store->runtime_storage_status->vol_list);
632 } else {
633 /*
634 * Need to cleanup but things are still referenced.
635 * We just remove the old changer_vol_list_t and on the next call to
636 * StorageReleaseVolList() this orphaned changer_vol_list_t will
637 * then be destroyed.
638 */
639 Dmsg0(100, "Need to free still referenced vol_list\n");
640 store->runtime_storage_status->vol_list =
641 (changer_vol_list_t*)malloc(sizeof(changer_vol_list_t));
642 changer_vol_list_t empty_vol_list;
643 *store->runtime_storage_status->vol_list = empty_vol_list;
644 }
645 }
646
647 /*
648 * Nothing cached or uncached data wanted so perform retrieval.
649 */
650 switch (store->Protocol) {
651 case APT_NATIVE:
652 contents = native_get_vol_list(ua, store, listall, scan);
653 break;
654 case APT_NDMPV2:
655 case APT_NDMPV3:
656 case APT_NDMPV4:
657 contents = ndmp_get_vol_list(ua, store, listall, scan);
658 break;
659 default:
660 break;
661 }
662
663 if (contents) {
664 /*
665 * Cache the returned content of the autochanger.
666 */
667 if (!store->runtime_storage_status->vol_list) {
668 store->runtime_storage_status->vol_list =
669 (changer_vol_list_t*)malloc(sizeof(changer_vol_list_t));
670 changer_vol_list_t empty_vol_list;
671 *store->runtime_storage_status->vol_list = empty_vol_list;
672 }
673 vol_list = store->runtime_storage_status->vol_list;
674 vol_list->reference_count++;
675 vol_list->contents = contents;
676 vol_list->timestamp = (utime_t)time(NULL);
677 vol_list->type = type;
678 }
679
680 bail_out:
681 V(store->runtime_storage_status->changer_lock);
682
683 return vol_list;
684 }
685
GetNumSlots(UaContext * ua,StorageResource * store)686 slot_number_t GetNumSlots(UaContext* ua, StorageResource* store)
687 {
688 slot_number_t slots = 0;
689
690 /*
691 * See if we can use the cached number of slots.
692 */
693 if (store->runtime_storage_status->slots > 0) {
694 return store->runtime_storage_status->slots;
695 }
696
697 P(store->runtime_storage_status->changer_lock);
698
699 switch (store->Protocol) {
700 case APT_NATIVE:
701 slots = NativeGetNumSlots(ua, store);
702 break;
703 case APT_NDMPV2:
704 case APT_NDMPV3:
705 case APT_NDMPV4:
706 slots = NdmpGetNumSlots(ua, store);
707 break;
708 default:
709 break;
710 }
711
712 store->runtime_storage_status->slots = slots;
713
714 V(store->runtime_storage_status->changer_lock);
715
716 return slots;
717 }
718
GetNumDrives(UaContext * ua,StorageResource * store)719 slot_number_t GetNumDrives(UaContext* ua, StorageResource* store)
720 {
721 drive_number_t drives = 0;
722
723 /*
724 * See if we can use the cached number of drives.
725 */
726 if (store->runtime_storage_status->drives > 0) {
727 return store->runtime_storage_status->drives;
728 }
729
730 P(store->runtime_storage_status->changer_lock);
731
732 switch (store->Protocol) {
733 case APT_NATIVE:
734 drives = NativeGetNumDrives(ua, store);
735 break;
736 case APT_NDMPV2:
737 case APT_NDMPV3:
738 case APT_NDMPV4:
739 drives = NdmpGetNumDrives(ua, store);
740 break;
741 default:
742 break;
743 }
744
745 store->runtime_storage_status->drives = drives;
746
747 V(store->runtime_storage_status->changer_lock);
748
749 return drives;
750 }
751
transfer_volume(UaContext * ua,StorageResource * store,slot_number_t src_slot,slot_number_t dst_slot)752 bool transfer_volume(UaContext* ua,
753 StorageResource* store,
754 slot_number_t src_slot,
755 slot_number_t dst_slot)
756 {
757 bool retval = false;
758
759 P(store->runtime_storage_status->changer_lock);
760
761 switch (store->Protocol) {
762 case APT_NATIVE:
763 retval = NativeTransferVolume(ua, store, src_slot, dst_slot);
764 break;
765 case APT_NDMPV2:
766 case APT_NDMPV3:
767 case APT_NDMPV4:
768 retval = NdmpTransferVolume(ua, store, src_slot, dst_slot);
769 break;
770 default:
771 break;
772 }
773
774 V(store->runtime_storage_status->changer_lock);
775
776 return retval;
777 }
778
DoAutochangerVolumeOperation(UaContext * ua,StorageResource * store,const char * operation,drive_number_t drive,slot_number_t slot)779 bool DoAutochangerVolumeOperation(UaContext* ua,
780 StorageResource* store,
781 const char* operation,
782 drive_number_t drive,
783 slot_number_t slot)
784 {
785 bool retval = false;
786
787 P(store->runtime_storage_status->changer_lock);
788
789 switch (store->Protocol) {
790 case APT_NATIVE:
791 retval =
792 NativeAutochangerVolumeOperation(ua, store, operation, drive, slot);
793 break;
794 case APT_NDMPV2:
795 case APT_NDMPV3:
796 case APT_NDMPV4:
797 retval =
798 NdmpAutochangerVolumeOperation(ua, store, operation, drive, slot);
799 break;
800 default:
801 break;
802 }
803
804 V(store->runtime_storage_status->changer_lock);
805
806 return retval;
807 }
808
809 /**
810 * See if a specific slot is loaded in one of the drives.
811 */
vol_is_loaded_in_drive(StorageResource * store,changer_vol_list_t * vol_list,slot_number_t slot)812 vol_list_t* vol_is_loaded_in_drive(StorageResource* store,
813 changer_vol_list_t* vol_list,
814 slot_number_t slot)
815 {
816 vol_list_t* vl;
817
818 vl = (vol_list_t*)vol_list->contents->first();
819 while (vl) {
820 switch (vl->slot_type) {
821 case slot_type_t::kSlotTypeDrive:
822 Dmsg2(100, "Checking drive %hd for loaded volume == %hd\n",
823 vl->bareos_slot_number, vl->currently_loaded_slot_number);
824 if (vl->currently_loaded_slot_number == slot) { return vl; }
825 break;
826 default:
827 break;
828 }
829 vl = (vol_list_t*)vol_list->contents->next((void*)vl);
830 }
831
832 return NULL;
833 }
834
835 /**
836 * Release the reference to the volume list returned from
837 * get_vol_list_from_storage()
838 */
StorageReleaseVolList(StorageResource * store,changer_vol_list_t * vol_list)839 void StorageReleaseVolList(StorageResource* store, changer_vol_list_t* vol_list)
840 {
841 P(store->runtime_storage_status->changer_lock);
842
843 Dmsg0(100, "Releasing volume list\n");
844
845 /*
846 * See if we are releasing a reference to the currently cached value.
847 */
848 if (store->runtime_storage_status->vol_list == vol_list) {
849 vol_list->reference_count--;
850 } else {
851 vol_list->reference_count--;
852 if (vol_list->reference_count == 0) {
853 /*
854 * It seems this is a release of an uncached version of the vol_list.
855 * We just destroy this vol_list as there are no more references to it.
856 */
857 FreeVolList(vol_list);
858 free(vol_list);
859 }
860 }
861
862 V(store->runtime_storage_status->changer_lock);
863 }
864
865 /**
866 * Destroy the volume list returned from get_vol_list_from_storage()
867 */
StorageFreeVolList(StorageResource * store,changer_vol_list_t * vol_list)868 void StorageFreeVolList(StorageResource* store, changer_vol_list_t* vol_list)
869 {
870 P(store->runtime_storage_status->changer_lock);
871
872 Dmsg1(100, "Freeing volume list at %p\n", vol_list);
873
874 FreeVolList(vol_list);
875
876 /*
877 * Clear the cached vol_list if needed.
878 */
879 if (store->runtime_storage_status->vol_list == vol_list) {
880 store->runtime_storage_status->vol_list = NULL;
881 }
882
883 V(store->runtime_storage_status->changer_lock);
884 }
885
886 /**
887 * Invalidate a cached volume list returned from get_vol_list_from_storage()
888 * Called by functions that change the content of the storage like mount,
889 * umount, release.
890 */
InvalidateVolList(StorageResource * store)891 void InvalidateVolList(StorageResource* store)
892 {
893 P(store->runtime_storage_status->changer_lock);
894
895 if (store->runtime_storage_status->vol_list) {
896 Dmsg1(100, "Invalidating volume list at %p\n",
897 store->runtime_storage_status->vol_list);
898
899 /*
900 * If the volume list is unreferenced we can destroy it otherwise we just
901 * reset the pointer and the StorageReleaseVolList() will destroy it for
902 * us the moment there are no more references.
903 */
904 if (store->runtime_storage_status->vol_list->reference_count == 0) {
905 FreeVolList(store->runtime_storage_status->vol_list);
906 } else {
907 store->runtime_storage_status->vol_list = NULL;
908 }
909 }
910
911 V(store->runtime_storage_status->changer_lock);
912 }
913
914
915 } /* namespace directordaemon */
916