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