1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2013 Free Software Foundation Europe e.V.
5    Copyright (C) 2015-2019 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  * Kern Sibbald, MM
24  * Split from reserve.c October 2008
25  */
26 /**
27  * @file
28  * Volume management functions for Storage Daemon
29  */
30 
31 #include "include/bareos.h"
32 #include "stored/stored.h"
33 #include "stored/stored_globals.h"
34 #include "stored/autochanger.h"
35 #include "include/jcr.h"
36 #include "lib/berrno.h"
37 
38 namespace storagedaemon {
39 
40 const int debuglevel = 150;
41 
42 static brwlock_t vol_list_lock;
43 static dlist* vol_list = NULL;
44 static dlist* read_vol_list = NULL;
45 static pthread_mutex_t read_vol_lock = PTHREAD_MUTEX_INITIALIZER;
46 
47 /* Global static variables */
48 #ifdef SD_DEBUG_LOCK
49 int vol_list_lock_count = 0;
50 int read_vol_list_lock_count = 0;
51 #else
52 static int vol_list_lock_count = 0;
53 static int read_vol_list_lock_count = 0;
54 #endif
55 
56 /* Forward referenced functions */
57 static void FreeVolItem(VolumeReservationItem* vol);
58 static void FreeReadVolItem(VolumeReservationItem* vol);
59 static VolumeReservationItem* new_vol_item(DeviceControlRecord* dcr,
60                                            const char* VolumeName);
61 static void DebugListVolumes(const char* imsg);
62 
63 /**
64  * For append volumes the key is the VolumeName.
65  */
CompareByVolumename(void * item1,void * item2)66 static int CompareByVolumename(void* item1, void* item2)
67 {
68   VolumeReservationItem* vol1 = (VolumeReservationItem*)item1;
69   VolumeReservationItem* vol2 = (VolumeReservationItem*)item2;
70 
71   ASSERT(vol1->vol_name);
72   ASSERT(vol2->vol_name);
73 
74   return strcmp(vol1->vol_name, vol2->vol_name);
75 }
76 
77 /**
78  * For read volumes the key is JobId, VolumeName.
79  */
ReadCompare(void * item1,void * item2)80 static int ReadCompare(void* item1, void* item2)
81 {
82   VolumeReservationItem* vol1 = (VolumeReservationItem*)item1;
83   VolumeReservationItem* vol2 = (VolumeReservationItem*)item2;
84 
85   ASSERT(vol1->vol_name);
86   ASSERT(vol2->vol_name);
87 
88   if (vol1->GetJobid() == vol2->GetJobid()) {
89     return strcmp(vol1->vol_name, vol2->vol_name);
90   }
91 
92   if (vol1->GetJobid() < vol2->GetJobid()) { return -1; }
93 
94   return 1;
95 }
96 
IsVolListEmpty()97 bool IsVolListEmpty() { return vol_list->empty(); }
98 
99 /**
100  *  Initialized the main volume list. Note, we are using a recursive lock.
101  */
InitVolListLock()102 void InitVolListLock()
103 {
104   int errstat;
105 
106   if ((errstat = RwlInit(&vol_list_lock, PRIO_SD_VOL_LIST)) != 0) {
107     BErrNo be;
108     Emsg1(M_ABORT, 0, _("Unable to initialize volume list lock. ERR=%s\n"),
109           be.bstrerror(errstat));
110   }
111 }
112 
TermVolListLock()113 void TermVolListLock() { RwlDestroy(&vol_list_lock); }
114 
115 /**
116  * This allows a given thread to recursively call to LockVolumes()
117  */
_lockVolumes(const char * file,int line)118 void _lockVolumes(const char* file, int line)
119 {
120   int errstat;
121 
122   vol_list_lock_count++;
123   if ((errstat = RwlWritelock_p(&vol_list_lock, file, line)) != 0) {
124     BErrNo be;
125     Emsg2(M_ABORT, 0, "RwlWritelock failure. stat=%d: ERR=%s\n", errstat,
126           be.bstrerror(errstat));
127   }
128 }
129 
_unLockVolumes()130 void _unLockVolumes()
131 {
132   int errstat;
133 
134   vol_list_lock_count--;
135   if ((errstat = RwlWriteunlock(&vol_list_lock)) != 0) {
136     BErrNo be;
137     Emsg2(M_ABORT, 0, "RwlWriteunlock failure. stat=%d: ERR=%s\n", errstat,
138           be.bstrerror(errstat));
139   }
140 }
141 
_lockReadVolumes(const char * file,int line)142 void _lockReadVolumes(const char* file, int line)
143 {
144   read_vol_list_lock_count++;
145   pthread_mutex_lock(&read_vol_lock);
146 }
147 
_unLockReadVolumes()148 void _unLockReadVolumes()
149 {
150   read_vol_list_lock_count--;
151   pthread_mutex_unlock(&read_vol_lock);
152 }
153 
154 /**
155  * Add a volume to the read list.
156  *
157  * Note, we use VolumeReservationItem because it simplifies the code
158  * even though, the only part of VolumeReservationItem that we need is
159  * the volume name.  The same volume may be in the list
160  * multiple times, but each one is distinguished by the
161  * JobId.  We use JobId, VolumeName as the key.
162  *
163  * We can get called multiple times for the same volume because
164  * when parsing the bsr, the volume name appears multiple times.
165  */
AddReadVolume(JobControlRecord * jcr,const char * VolumeName)166 void AddReadVolume(JobControlRecord* jcr, const char* VolumeName)
167 {
168   VolumeReservationItem *nvol, *vol;
169 
170   nvol = new_vol_item(NULL, VolumeName);
171   nvol->SetJobid(jcr->JobId);
172   nvol->SetReading();
173   LockReadVolumes();
174   vol = (VolumeReservationItem*)read_vol_list->binary_insert(nvol, ReadCompare);
175   if (vol != nvol) {
176     FreeReadVolItem(nvol);
177     Dmsg2(debuglevel, "read_vol=%s JobId=%d already in list.\n", VolumeName,
178           jcr->JobId);
179   } else {
180     Dmsg2(debuglevel, "add_read_vol=%s JobId=%d\n", VolumeName, jcr->JobId);
181   }
182   UnlockReadVolumes();
183 }
184 
185 /**
186  * Remove a given volume name from the read list.
187  */
RemoveReadVolume(JobControlRecord * jcr,const char * VolumeName)188 void RemoveReadVolume(JobControlRecord* jcr, const char* VolumeName)
189 {
190   VolumeReservationItem vol, *fvol;
191 
192   LockReadVolumes();
193   vol.vol_name = strdup(VolumeName);
194   vol.SetJobid(jcr->JobId);
195 
196   fvol =
197       (VolumeReservationItem*)read_vol_list->binary_search(&vol, ReadCompare);
198   free(vol.vol_name);
199 
200   if (fvol) {
201     Dmsg3(debuglevel, "remove_read_vol=%s JobId=%d found=%d\n", VolumeName,
202           jcr->JobId, fvol != NULL);
203   }
204   if (fvol) {
205     read_vol_list->remove(fvol);
206     FreeReadVolItem(fvol);
207   }
208   UnlockReadVolumes();
209   // pthread_cond_broadcast(&wait_next_vol);
210 }
211 
212 /**
213  * Search for a Volume name in the read Volume list.
214  *
215  * Returns: VolumeReservationItem entry on success
216  *          NULL if the Volume is not in the list
217  */
find_read_volume(const char * VolumeName)218 static VolumeReservationItem* find_read_volume(const char* VolumeName)
219 {
220   VolumeReservationItem vol, *fvol;
221 
222   if (read_vol_list->empty()) {
223     Dmsg0(debuglevel, "find_read_vol: read_vol_list empty.\n");
224     return NULL;
225   }
226 
227   /*
228    * Do not lock reservations here
229    */
230   LockReadVolumes();
231   vol.vol_name = strdup(VolumeName);
232 
233   /*
234    * Note, we do want a simple CompareByVolumename on volume name only here
235    */
236   fvol = (VolumeReservationItem*)read_vol_list->binary_search(
237       &vol, CompareByVolumename);
238   free(vol.vol_name);
239 
240   Dmsg2(debuglevel, "find_read_vol=%s found=%d\n", VolumeName, fvol != NULL);
241   UnlockReadVolumes();
242 
243   return fvol;
244 }
245 
246 /**
247  * List Volumes -- this should be moved to status.c
248  */
249 enum
250 {
251   debug_lock = true,
252   debug_nolock = false
253 };
254 
DebugListVolumes(const char * imsg)255 static void DebugListVolumes(const char* imsg)
256 {
257   VolumeReservationItem* vol;
258   PoolMem msg(PM_MESSAGE);
259 
260   foreach_vol (vol) {
261     if (vol->dev) {
262       Mmsg(msg, "List %s: %s in_use=%d swap=%d on device %s\n", imsg,
263            vol->vol_name, vol->IsInUse(), vol->IsSwapping(),
264            vol->dev->print_name());
265     } else {
266       Mmsg(msg, "List %s: %s in_use=%d swap=%d no dev\n", imsg, vol->vol_name,
267            vol->IsInUse(), vol->IsSwapping());
268     }
269     Dmsg1(debuglevel, "%s", msg.c_str());
270   }
271   endeach_vol(vol);
272 }
273 
274 /**
275  * Create a Volume item to put in the Volume list
276  * Ensure that the device points to it.
277  */
new_vol_item(DeviceControlRecord * dcr,const char * VolumeName)278 static VolumeReservationItem* new_vol_item(DeviceControlRecord* dcr,
279                                            const char* VolumeName)
280 {
281   VolumeReservationItem* vol;
282   VolumeReservationItem emptyVol;
283 
284   vol = (VolumeReservationItem*)malloc(sizeof(VolumeReservationItem));
285   *vol = emptyVol;
286   vol->vol_name = strdup(VolumeName);
287   if (dcr) {
288     vol->dev = dcr->dev;
289     Dmsg3(debuglevel, "new Vol=%s at %p dev=%s\n", VolumeName, vol->vol_name,
290           vol->dev->print_name());
291   }
292   vol->InitMutex();
293   vol->IncUseCount();
294 
295   return vol;
296 }
297 
FreeVolItem(VolumeReservationItem * vol)298 static void FreeVolItem(VolumeReservationItem* vol)
299 {
300   Device* dev = NULL;
301 
302   vol->DecUseCount();
303   vol->Lock();
304   if (vol->UseCount() > 0) {
305     vol->Unlock();
306     return;
307   }
308   vol->Unlock();
309   free(vol->vol_name);
310   if (vol->dev) { dev = vol->dev; }
311   vol->DestroyMutex();
312   free(vol);
313   if (dev) { dev->vol = NULL; }
314 }
315 
FreeReadVolItem(VolumeReservationItem * vol)316 static void FreeReadVolItem(VolumeReservationItem* vol)
317 {
318   Device* dev = NULL;
319 
320   vol->DecUseCount();
321   vol->Lock();
322   if (vol->UseCount() > 0) {
323     vol->Unlock();
324     return;
325   }
326   vol->Unlock();
327   free(vol->vol_name);
328   if (vol->dev) { dev = vol->dev; }
329   vol->DestroyMutex();
330   free(vol);
331   if (dev) { dev->vol = NULL; }
332 }
333 
334 /**
335  * Put a new Volume entry in the Volume list. This
336  * effectively reserves the volume so that it will
337  * not be mounted again.
338  *
339  * If the device has any current volume associated with it,
340  * and it is a different Volume, and the device is not busy,
341  * we release the old Volume item and insert the new one.
342  *
343  * It is assumed that the device is free and locked so that
344  * we can change the device structure.
345  *
346  * Some details of the Volume list handling:
347  *
348  *  1. The Volume list entry is attached to the drive (rather than
349  *     attached to a job as it was previously. I.e. the drive that "owns"
350  *     the volume (in use, mounted)
351  *     must point to the volume (still to be maintained in a list).
352  *
353  *  2. The Volume is entered in the list when a drive is reserved.
354  *
355  *  3. When a drive is in use, the device code must appropriately update the
356  *     volume name as it changes.
357  *
358  *     This code keeps the same list entry as long as the drive
359  *     has any volume associated with it but the volume name in the list
360  *     must be updated when the drive has a different volume mounted.
361  *
362  *  4. A job that has reserved a volume, can un-reserve the volume, and if the
363  *     volume is not mounted, and not reserved, and not in use, it will be
364  *     removed from the list.
365  *
366  *  5. If a job wants to reserve a drive with a different Volume from the one on
367  *     the drive, it can re-use the drive for the new Volume.
368  *
369  *  6. If a job wants a Volume that is in a different drive, it can either use
370  * the other drive or take the volume, only if the other drive is not in use or
371  *     not reserved.
372  *
373  *  One nice aspect of this is that the reserve use count and the writer use
374  * count already exist and are correctly programmed and will need no changes --
375  * use counts are always very tricky.
376  *
377  *  The old code had a concept of "reserving" a Volume, but was changed
378  *  to reserving and using a drive.  A volume is must be attached to (owned by)
379  * a drive and can move from drive to drive or be unused given certain specific
380  *  conditions of the drive.  The key is that the drive must "own" the Volume.
381  *
382  *  Return: VolumeReservationItem entry on success
383  *          NULL volume busy on another drive
384  */
reserve_volume(DeviceControlRecord * dcr,const char * VolumeName)385 VolumeReservationItem* reserve_volume(DeviceControlRecord* dcr,
386                                       const char* VolumeName)
387 {
388   VolumeReservationItem *vol, *nvol;
389   Device* volatile dev = dcr->dev;
390 
391   if (JobCanceled(dcr->jcr)) { return NULL; }
392   ASSERT(dev != NULL);
393 
394   Dmsg2(debuglevel, "enter reserve_volume=%s drive=%s\n", VolumeName,
395         dcr->dev->print_name());
396 
397   /*
398    * If aquiring a volume for writing it may not be on the read volume list.
399    */
400   if (me->filedevice_concurrent_read && dcr->IsWriting() &&
401       find_read_volume(VolumeName)) {
402     Mmsg(dcr->jcr->errmsg,
403          _("Could not reserve volume \"%s\" for append, because it is read by "
404            "another Job.\n"),
405          dev->VolHdr.VolumeName);
406     return NULL;
407   }
408 
409   /*
410    * We lock the reservations system here to ensure when adding a new volume
411    * that no newly scheduled job can reserve it.
412    */
413   LockVolumes();
414   if (debug_level >= debuglevel) { DebugListVolumes("begin reserve_volume"); }
415 
416   /*
417    * First, remove any old volume attached to this device as it is no longer
418    * used.
419    */
420   if (dev->vol) {
421     vol = dev->vol;
422     Dmsg4(debuglevel, "Vol attached=%s, newvol=%s volinuse=%d on %s\n",
423           vol->vol_name, VolumeName, vol->IsInUse(), dev->print_name());
424     /*
425      * Make sure we don't remove the current volume we are inserting
426      * because it was probably inserted by another job, or it
427      * is not being used and is marked as not reserved.
428      */
429     if (bstrcmp(vol->vol_name, VolumeName)) {
430       Dmsg2(debuglevel, "=== set reserved vol=%s dev=%s\n", VolumeName,
431             vol->dev->print_name());
432       goto get_out; /* Volume already on this device */
433     } else {
434       /*
435        * Don't release a volume if it was reserved by someone other than us
436        */
437       if (vol->IsInUse() && !dcr->reserved_volume) {
438         Dmsg1(debuglevel, "Cannot free vol=%s. It is reserved.\n",
439               vol->vol_name);
440         vol = NULL; /* vol in use */
441         goto get_out;
442       }
443       Dmsg2(debuglevel, "reserve_vol free vol=%s at %p\n", vol->vol_name,
444             vol->vol_name);
445 
446       /*
447        * If old Volume is still mounted, must unload it
448        */
449       if (bstrcmp(vol->vol_name, dev->VolHdr.VolumeName)) {
450         Dmsg0(50, "SetUnload\n");
451         dev->SetUnload(); /* have to unload current volume */
452       }
453       FreeVolume(dev); /* Release old volume entry */
454 
455       if (debug_level >= debuglevel) { DebugListVolumes("reserve_vol free"); }
456     }
457   }
458 
459   /*
460    * Create a new Volume entry
461    */
462   nvol = new_vol_item(dcr, VolumeName);
463 
464   /*
465    * See if this is a request for reading a file type device which can be
466    * accesses by multiple readers at once without disturbing each other.
467    */
468   if (me->filedevice_concurrent_read && !dcr->IsWriting() && dev->IsFile()) {
469     nvol->SetJobid(dcr->jcr->JobId);
470     nvol->SetReading();
471     vol = nvol;
472     dev->vol = vol;
473 
474     /*
475      * Read volumes on file based devices are not inserted into the write volume
476      * list.
477      */
478     goto get_out;
479   } else {
480     /*
481      * Now try to insert the new Volume
482      */
483     vol = (VolumeReservationItem*)vol_list->binary_insert(nvol,
484                                                           CompareByVolumename);
485   }
486 
487   if (vol != nvol) {
488     Dmsg2(debuglevel, "Found vol=%s dev-same=%d\n", vol->vol_name,
489           dev == vol->dev);
490 
491     /*
492      * At this point, a Volume with this name already is in the list,
493      * so we simply release our new Volume entry. Note, this should
494      * only happen if we are moving the volume from one drive to another.
495      */
496     Dmsg2(debuglevel, "reserve_vol free-tmp vol=%s at %p\n", vol->vol_name,
497           vol->vol_name);
498 
499     /*
500      * Clear dev pointer so that FreeVolItem() doesn't take away our volume.
501      */
502     nvol->dev = NULL; /* don't zap dev entry */
503     FreeVolItem(nvol);
504 
505     if (vol->dev) {
506       Dmsg2(debuglevel, "dev=%s vol->dev=%s\n", dev->print_name(),
507             vol->dev->print_name());
508     }
509 
510     /*
511      * Check if we are trying to use the Volume on a different drive dev is our
512      * device vol->dev is where the Volume we want is
513      */
514     if (dev != vol->dev) {
515       /*
516        * Caller wants to switch Volume to another device
517        */
518       if (!vol->dev->IsBusy() && !vol->IsSwapping()) {
519         slot_number_t slot;
520 
521         Dmsg3(debuglevel, "==== Swap vol=%s from dev=%s to %s\n", VolumeName,
522               vol->dev->print_name(), dev->print_name());
523         FreeVolume(dev); /* free any volume attached to our drive */
524         Dmsg1(50, "SetUnload dev=%s\n", dev->print_name());
525         dev->SetUnload();      /* Unload any volume that is on our drive */
526         dcr->SetDev(vol->dev); /* temp point to other dev */
527         slot = GetAutochangerLoadedSlot(dcr); /* get slot on other drive */
528         dcr->SetDev(dev);                     /* restore dev */
529         vol->SetSlotNumber(slot);             /* save slot */
530         vol->dev->SetUnload();                /* unload the other drive */
531         vol->SetSwapping();                   /* swap from other drive */
532         dev->swap_dev = vol->dev;             /* remember to get this vol */
533         dev->SetLoad();                       /* then reload on our drive */
534         vol->dev->vol = NULL; /* remove volume from other drive */
535         vol->dev = dev;       /* point the Volume at our drive */
536         dev->vol = vol;       /* point our drive at the Volume */
537       } else {
538         Jmsg7(dcr->jcr, M_WARNING, 0,
539               "Need volume from other drive, but swap not possible. "
540               "Status: read=%d num_writers=%d num_reserve=%d swap=%d "
541               "vol=%s from dev=%s to %s\n",
542               vol->dev->CanRead(), vol->dev->num_writers,
543               vol->dev->NumReserved(), vol->IsSwapping(), VolumeName,
544               vol->dev->print_name(), dev->print_name());
545         if (vol->IsSwapping() && dev->swap_dev) {
546           Dmsg3(debuglevel, "Swap failed vol=%s from=%s to dev=%s\n",
547                 vol->vol_name, dev->swap_dev->print_name(), dev->print_name());
548         } else {
549           Dmsg3(debuglevel, "Swap failed vol=%s from=%p to dev=%s\n",
550                 vol->vol_name, dev->swap_dev, dev->print_name());
551         }
552 
553         if (debug_level >= debuglevel) { DebugListVolumes("failed swap"); }
554 
555         vol = NULL; /* device busy */
556         goto get_out;
557       }
558     } else {
559       dev->vol = vol;
560     }
561   } else {
562     dev->vol = vol; /* point to newly inserted volume */
563   }
564 
565 get_out:
566   if (vol) {
567     Dmsg2(debuglevel, "=== set in_use. vol=%s dev=%s\n", vol->vol_name,
568           vol->dev->print_name());
569     vol->SetInUse();
570     dcr->reserved_volume = true;
571     bstrncpy(dcr->VolumeName, vol->vol_name, sizeof(dcr->VolumeName));
572   }
573 
574   if (debug_level >= debuglevel) { DebugListVolumes("end new volume"); }
575 
576   UnlockVolumes();
577   return vol;
578 }
579 
580 /**
581  * Start walk of vol chain
582  * The proper way to walk the vol chain is:
583  *
584  * VolumeReservationItem *vol;
585  * foreach_vol(vol) {
586  *    ...
587  * }
588  * endeach_vol(vol);
589  *
590  * It is possible to leave out the endeach_vol(vol), but in that case,
591  * the last vol referenced must be explicitly released with:
592  *
593  * FreeVolItem(vol);
594  */
vol_walk_start()595 VolumeReservationItem* vol_walk_start()
596 {
597   VolumeReservationItem* vol;
598   LockVolumes();
599   vol = (VolumeReservationItem*)vol_list->first();
600   if (vol) {
601     vol->IncUseCount();
602     Dmsg2(debuglevel, "Inc walk_start UseCount=%d volname=%s\n",
603           vol->UseCount(), vol->vol_name);
604   }
605   UnlockVolumes();
606 
607   return vol;
608 }
609 
610 /**
611  * Get next vol from chain, and release current one
612  */
VolWalkNext(VolumeReservationItem * prev_vol)613 VolumeReservationItem* VolWalkNext(VolumeReservationItem* prev_vol)
614 {
615   VolumeReservationItem* vol;
616 
617   LockVolumes();
618   vol = (VolumeReservationItem*)vol_list->next(prev_vol);
619   if (vol) {
620     vol->IncUseCount();
621     Dmsg2(debuglevel, "Inc walk_next UseCount=%d volname=%s\n", vol->UseCount(),
622           vol->vol_name);
623   }
624   if (prev_vol) { FreeVolItem(prev_vol); }
625   UnlockVolumes();
626 
627   return vol;
628 }
629 
630 /**
631  * Release last vol referenced
632  */
VolWalkEnd(VolumeReservationItem * vol)633 void VolWalkEnd(VolumeReservationItem* vol)
634 {
635   if (vol) {
636     LockVolumes();
637     Dmsg2(debuglevel, "Free walk_end UseCount=%d volname=%s\n", vol->UseCount(),
638           vol->vol_name);
639     FreeVolItem(vol);
640     UnlockVolumes();
641   }
642 }
643 
644 /*
645  * Start walk of vol chain
646  * The proper way to walk the vol chain is:
647  *
648  * VolumeReservationItem *vol;
649  * foreach_read_vol(vol) {
650  *    ...
651  * }
652  * endeach_read_vol(vol);
653  *
654  * It is possible to leave out the endeach_read_vol(vol), but in that case,
655  * the last vol referenced must be explicitly released with:
656  *
657  * FreeReadVolItem(vol);
658  */
read_vol_walk_start()659 VolumeReservationItem* read_vol_walk_start()
660 {
661   VolumeReservationItem* vol;
662   LockReadVolumes();
663   vol = (VolumeReservationItem*)read_vol_list->first();
664   if (vol) {
665     vol->IncUseCount();
666     Dmsg2(debuglevel, "Inc walk_start UseCount=%d volname=%s\n",
667           vol->UseCount(), vol->vol_name);
668   }
669   UnlockReadVolumes();
670 
671   return vol;
672 }
673 
674 /*
675  * Get next vol from chain, and release current one
676  */
ReadVolWalkNext(VolumeReservationItem * prev_vol)677 VolumeReservationItem* ReadVolWalkNext(VolumeReservationItem* prev_vol)
678 {
679   VolumeReservationItem* vol;
680 
681   LockReadVolumes();
682   vol = (VolumeReservationItem*)read_vol_list->next(prev_vol);
683   if (vol) {
684     vol->IncUseCount();
685     Dmsg2(debuglevel, "Inc walk_next UseCount=%d volname=%s\n", vol->UseCount(),
686           vol->vol_name);
687   }
688   if (prev_vol) { FreeReadVolItem(prev_vol); }
689   UnlockReadVolumes();
690 
691   return vol;
692 }
693 
694 /*
695  * Release last vol referenced
696  */
ReadVolWalkEnd(VolumeReservationItem * vol)697 void ReadVolWalkEnd(VolumeReservationItem* vol)
698 {
699   if (vol) {
700     LockReadVolumes();
701     Dmsg2(debuglevel, "Free walk_end UseCount=%d volname=%s\n", vol->UseCount(),
702           vol->vol_name);
703     FreeReadVolItem(vol);
704     UnlockReadVolumes();
705   }
706 }
707 
708 /**
709  * Search for a Volume name in the Volume list.
710  *
711  * Returns: VolumeReservationItem entry on success
712  *          NULL if the Volume is not in the list
713  */
find_volume(const char * VolumeName)714 static VolumeReservationItem* find_volume(const char* VolumeName)
715 {
716   VolumeReservationItem vol, *fvol;
717 
718   if (vol_list->empty()) { return NULL; }
719   /* Do not lock reservations here */
720   LockVolumes();
721   vol.vol_name = strdup(VolumeName);
722   fvol = (VolumeReservationItem*)vol_list->binary_search(&vol,
723                                                          CompareByVolumename);
724   free(vol.vol_name);
725   Dmsg2(debuglevel, "find_vol=%s found=%d\n", VolumeName, fvol != NULL);
726 
727   if (debug_level >= debuglevel) { DebugListVolumes("find_volume"); }
728 
729   UnlockVolumes();
730   return fvol;
731 }
732 
733 /**
734  * Free a Volume from the Volume list if it is no longer used
735  * Note, for tape drives we want to remember where the Volume
736  * was when last used, so rather than free the volume entry,
737  * we simply mark it "not reserved" so when the drive is really
738  * needed for another volume, we can reuse it.
739  *
740  * Returns: true if the Volume found and "removed" from the list
741  *          false if the Volume is not in the list or is in use
742  */
VolumeUnused(DeviceControlRecord * dcr)743 bool VolumeUnused(DeviceControlRecord* dcr)
744 {
745   Device* dev = dcr->dev;
746 
747   if (!dev->vol) {
748     Dmsg1(debuglevel, "vol_unused: no vol on %s\n", dev->print_name());
749     if (debug_level >= debuglevel) {
750       DebugListVolumes("null vol cannot unreserve_volume");
751     }
752 
753     return false;
754   }
755 
756   Dmsg1(debuglevel, "=== clear in_use vol=%s\n", dev->vol->vol_name);
757   dev->vol->ClearInUse();
758 
759   if (dev->vol->IsSwapping()) {
760     Dmsg1(debuglevel, "vol_unused: vol being swapped on %s\n",
761           dev->print_name());
762 
763     if (debug_level >= debuglevel) {
764       DebugListVolumes("swapping vol cannot FreeVolume");
765     }
766     return false;
767   }
768 
769   /*
770    * If this is a tape, we do not free the volume, rather we wait
771    * until the autoloader unloads it, or until another tape is
772    * explicitly read in this drive. This allows the SD to remember
773    * where the tapes are or last were.
774    */
775   Dmsg4(debuglevel,
776         "=== set not reserved vol=%s num_writers=%d dev_reserved=%d dev=%s\n",
777         dev->vol->vol_name, dev->num_writers, dev->NumReserved(),
778         dev->print_name());
779   if (dev->IsTape() || dev->IsAutochanger()) {
780     return true;
781   } else {
782     /*
783      * Note, this frees the volume reservation entry, but the file descriptor
784      * remains open with the OS.
785      */
786     return FreeVolume(dev);
787   }
788 }
789 
790 /**
791  * Unconditionally release the volume entry
792  */
FreeVolume(Device * dev)793 bool FreeVolume(Device* dev)
794 {
795   VolumeReservationItem* vol;
796 
797   LockVolumes();
798   vol = dev->vol;
799   if (vol == NULL) {
800     Dmsg1(debuglevel, "No vol on dev %s\n", dev->print_name());
801     UnlockVolumes();
802     return false;
803   }
804 
805   /*
806    * Don't free a volume while it is being swapped
807    */
808   if (!vol->IsSwapping()) {
809     Dmsg1(debuglevel, "=== clear in_use vol=%s\n", vol->vol_name);
810     dev->vol = NULL;
811 
812     /*
813      * Volume is on write volume list if one of the folling is applicable:
814      *  - The volume is written to.
815      *  - Config option filedevice_concurrent_read is not on.
816      *  - The device is not of type File.
817      */
818     if (vol->IsWriting() || !me->filedevice_concurrent_read || !dev->IsFile()) {
819       vol_list->remove(vol);
820     }
821     Dmsg2(debuglevel, "=== remove volume %s dev=%s\n", vol->vol_name,
822           dev->print_name());
823     FreeVolItem(vol);
824 
825     if (debug_level >= debuglevel) { DebugListVolumes("FreeVolume"); }
826   } else {
827     Dmsg1(debuglevel, "=== cannot clear swapping vol=%s\n", vol->vol_name);
828   }
829   UnlockVolumes();
830   // pthread_cond_broadcast(&wait_next_vol);
831 
832   return true;
833 }
834 
835 /**
836  * Create the Volume list
837  */
CreateVolumeLists()838 void CreateVolumeLists()
839 {
840   VolumeReservationItem* vol = NULL;
841   if (vol_list == NULL) { vol_list = new dlist(vol, &vol->link); }
842   if (read_vol_list == NULL) { read_vol_list = new dlist(vol, &vol->link); }
843 }
844 
845 /**
846  * Free normal append volumes list
847  */
FreeVolumeList(const char * what,dlist * vollist)848 static inline void FreeVolumeList(const char* what, dlist* vollist)
849 {
850   VolumeReservationItem* vol;
851 
852   foreach_dlist (vol, vollist) {
853     if (vol->dev) {
854       Dmsg3(debuglevel, "free %s Volume=%s dev=%s\n", what, vol->vol_name,
855             vol->dev->print_name());
856     } else {
857       Dmsg2(debuglevel, "free %s Volume=%s No dev\n", what, vol->vol_name);
858     }
859     free(vol->vol_name);
860     vol->vol_name = NULL;
861     vol->DestroyMutex();
862   }
863 }
864 
865 /**
866  * Release all Volumes from the list
867  */
FreeVolumeLists()868 void FreeVolumeLists()
869 {
870   if (vol_list) {
871     LockVolumes();
872     FreeVolumeList("vol_list", vol_list);
873     delete vol_list;
874     vol_list = NULL;
875     UnlockVolumes();
876   }
877 
878   if (read_vol_list) {
879     LockReadVolumes();
880     FreeVolumeList("read_vol_list", read_vol_list);
881     delete read_vol_list;
882     read_vol_list = NULL;
883     UnlockReadVolumes();
884   }
885 }
886 
887 /**
888  * Determine if caller can write on volume
889  */
Can_i_write_volume()890 bool DeviceControlRecord::Can_i_write_volume()
891 {
892   VolumeReservationItem* vol;
893 
894   vol = find_read_volume(VolumeName);
895   if (vol) {
896     Dmsg1(100, "Found in read list; cannot write vol=%s\n", VolumeName);
897     return false;
898   }
899 
900   return Can_i_use_volume();
901 }
902 
903 /**
904  * Determine if caller can read or write volume
905  */
Can_i_use_volume()906 bool DeviceControlRecord::Can_i_use_volume()
907 {
908   bool rtn = true;
909   VolumeReservationItem* vol;
910 
911   if (JobCanceled(jcr)) { return false; }
912   LockVolumes();
913   vol = find_volume(VolumeName);
914   if (!vol) {
915     Dmsg1(debuglevel, "Vol=%s not in use.\n", VolumeName);
916     goto get_out; /* vol not in list */
917   }
918   ASSERT(vol->dev != NULL);
919 
920   if (dev == vol->dev) { /* same device OK */
921     Dmsg1(debuglevel, "Vol=%s on same dev.\n", VolumeName);
922     goto get_out;
923   } else {
924     Dmsg3(debuglevel, "Vol=%s on %s we have %s\n", VolumeName,
925           vol->dev->print_name(), dev->print_name());
926   }
927   /* ***FIXME*** check this ... */
928   if (!vol->dev->IsBusy()) {
929     Dmsg2(debuglevel, "Vol=%s dev=%s not busy.\n", VolumeName,
930           vol->dev->print_name());
931     goto get_out;
932   } else {
933     Dmsg2(debuglevel, "Vol=%s dev=%s busy.\n", VolumeName,
934           vol->dev->print_name());
935   }
936   Dmsg2(debuglevel, "Vol=%s in use by %s.\n", VolumeName,
937         vol->dev->print_name());
938   rtn = false;
939 
940 get_out:
941   UnlockVolumes();
942   return rtn;
943 }
944 
945 /**
946  * Create a temporary copy of the volume list.  We do this,
947  * to avoid having the volume list locked during the
948  * call to ReserveDevice(), which would cause a deadlock.
949  *
950  * Note, we may want to add an update counter on the vol_list
951  * so that if it is modified while we are traversing the copy
952  * we can take note and act accordingly (probably redo the
953  * search at least a few times).
954  */
dup_vol_list(JobControlRecord * jcr)955 dlist* dup_vol_list(JobControlRecord* jcr)
956 {
957   dlist* temp_vol_list;
958   VolumeReservationItem* vol = NULL;
959 
960   Dmsg0(debuglevel, "lock volumes\n");
961 
962   Dmsg0(debuglevel, "duplicate vol list\n");
963   temp_vol_list = new dlist(vol, &vol->link);
964   foreach_vol (vol) {
965     VolumeReservationItem *nvol, *tvol;
966 
967     tvol = new_vol_item(NULL, vol->vol_name);
968     tvol->dev = vol->dev;
969     nvol = (VolumeReservationItem*)temp_vol_list->binary_insert(
970         tvol, CompareByVolumename);
971     if (tvol != nvol) {
972       tvol->dev = NULL; /* don't zap dev entry */
973       FreeVolItem(tvol);
974       Pmsg0(000, "Logic error. Duplicating vol list hit duplicate.\n");
975       Jmsg(jcr, M_WARNING, 0,
976            "Logic error. Duplicating vol list hit duplicate.\n");
977     }
978   }
979   endeach_vol(vol);
980   Dmsg0(debuglevel, "unlock volumes\n");
981 
982   return temp_vol_list;
983 }
984 
985 /**
986  * Free the specified temp list.
987  */
FreeTempVolList(dlist * temp_vol_list)988 void FreeTempVolList(dlist* temp_vol_list)
989 {
990   FreeVolumeList("temp_vol_list", temp_vol_list);
991   delete temp_vol_list;
992 }
993 
994 } /* namespace storagedaemon */
995