]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/vol_mgr.c
kes Add read volume list code to SD -- not yet used.
[bacula/bacula] / bacula / src / stored / vol_mgr.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU 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    General Public License for more details.
17
18    You should have received a copy of the GNU 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    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *   Volume management functions for Storage Daemon
30  *
31  *   Kern Sibbald, MM
32  *
33  *   Split from reserve.c October 2008
34  *
35  *   Version $Id: reserve.c 7380 2008-07-14 10:42:59Z kerns $
36  *
37  */
38
39 #include "bacula.h"
40 #include "stored.h"
41
42 const int dbglvl =  50;
43
44 static dlist *vol_list = NULL;
45 static brwlock_t vol_list_lock;
46 static dlist *read_vol_list = NULL;
47 static pthread_mutex_t read_vol_lock = PTHREAD_MUTEX_INITIALIZER;
48
49 /* Forward referenced functions */
50 static void free_vol_item(VOLRES *vol);
51 static VOLRES *new_vol_item(DCR *dcr, const char *VolumeName);
52
53 /*
54  * For append volumes the key is the VolumeName.
55  */
56 static int my_compare(void *item1, void *item2)
57 {
58    return strcmp(((VOLRES *)item1)->vol_name, ((VOLRES *)item2)->vol_name);
59 }
60
61 /*
62  * For read volumes the key is JobId, VolumeName.
63  */
64 static int read_compare(void *item1, void *item2)
65 {
66    VOLRES *vol1 = (VOLRES *)item1;
67    VOLRES *vol2 = (VOLRES *)item2;
68
69    if (vol1->get_jobid() == vol2->get_jobid()) {
70       return strcmp(vol1->vol_name, vol2->vol_name);
71    }
72    if (vol1->get_jobid() < vol2->get_jobid()) {
73       return -1;
74    }
75    return 1;
76 }
77
78
79 bool is_vol_list_empty() 
80 {
81    return vol_list->empty();
82 }
83
84 int vol_list_lock_count = 0;
85
86 /*
87  *  Initialized the main volume list. Note, we are using a recursive lock.
88  */
89 void init_vol_list_lock()
90 {
91    int errstat;
92    if ((errstat=rwl_init(&vol_list_lock)) != 0) {
93       berrno be;
94       Emsg1(M_ABORT, 0, _("Unable to initialize volume list lock. ERR=%s\n"),
95             be.bstrerror(errstat));
96    }
97 }
98
99 void term_vol_list_lock()
100 {
101    rwl_destroy(&vol_list_lock);
102 }
103
104
105
106 /* 
107  * This allows a given thread to recursively call to lock_volumes()
108  */
109 void _lock_volumes()
110 {
111    int errstat;
112    vol_list_lock_count++;
113    if ((errstat=rwl_writelock(&vol_list_lock)) != 0) {
114       berrno be;
115       Emsg2(M_ABORT, 0, "rwl_writelock failure. stat=%d: ERR=%s\n",
116            errstat, be.bstrerror(errstat));
117    }
118 }
119
120 void _unlock_volumes()
121 {
122    int errstat;
123    vol_list_lock_count--;
124    if ((errstat=rwl_writeunlock(&vol_list_lock)) != 0) {
125       berrno be;
126       Emsg2(M_ABORT, 0, "rwl_writeunlock failure. stat=%d: ERR=%s\n",
127            errstat, be.bstrerror(errstat));
128    }
129 }
130
131 void lock_read_volumes()
132 {
133    P(read_vol_lock);
134 }
135
136 void unlock_read_volumes()
137 {
138    V(read_vol_lock);
139 }
140
141 /*
142  * Add a volume to the read list.
143  * Note, we use VOLRES because it simplifies the code
144  *   even though, the only part of VOLRES that we need is
145  *   the volume name.  The same volume may be in the list
146  *   multiple times, but each one is distinguished by the 
147  *   JobId.  We use JobId, VolumeName as the key.
148  * We can get called multiple times for the same volume because
149  *   when parsing the bsr, the volume name appears multiple times.
150  */
151 void add_read_volume(JCR *jcr, const char *VolumeName)
152 {
153    VOLRES *nvol, *vol;
154
155    lock_read_volumes();
156    nvol = new_vol_item(NULL, VolumeName);
157    nvol->set_jobid(jcr->JobId);
158    vol = (VOLRES *)read_vol_list->binary_insert(nvol, read_compare);
159    if (vol != nvol) {
160       free_vol_item(nvol);
161       Dmsg2(1, "read_vol=%s JobId=%d already in list.\n", VolumeName, jcr->JobId);
162    } else {
163       Dmsg2(1, "add read_vol=%s JobId=%d\n", VolumeName, jcr->JobId);
164    }
165    unlock_read_volumes();
166 }
167
168 /*
169  * Remove a given volume name from the read list.
170  */
171 void remove_read_volume(JCR *jcr, const char *VolumeName)
172 {
173    VOLRES vol, *fvol;
174    lock_read_volumes();
175    vol.vol_name = bstrdup(VolumeName);
176    vol.set_jobid(jcr->JobId);
177    fvol = (VOLRES *)read_vol_list->binary_search(&vol, read_compare);
178    free(vol.vol_name);
179    Dmsg3(1, "remove_read_vol=%s JobId=%d found=%d\n", VolumeName, jcr->JobId, fvol!=NULL);
180    debug_list_volumes("remove_read_volume");
181    if (fvol) {
182       read_vol_list->remove(fvol);
183       free_vol_item(fvol);
184    }
185    unlock_read_volumes();
186 }
187
188 /*
189  * List Volumes -- this should be moved to status.c
190  */
191 enum {
192    debug_lock = true,
193    debug_nolock = false
194 };
195
196 void debug_list_volumes(const char *imsg)
197 {
198    VOLRES *vol;
199    POOL_MEM msg(PM_MESSAGE);
200
201    lock_volumes();
202    foreach_dlist(vol, vol_list) {
203       if (vol->dev) {
204          Mmsg(msg, "List %s: %s in_use=%d on device %s\n", imsg, 
205               vol->vol_name, vol->is_in_use(), vol->dev->print_name());
206       } else {
207          Mmsg(msg, "List %s: %s in_use=%d no dev\n", imsg, vol->vol_name, 
208               vol->is_in_use());
209       }
210       Dmsg1(dbglvl, "%s", msg.c_str());
211    }
212
213    unlock_volumes();
214 }
215
216
217 /*
218  * List Volumes -- this should be moved to status.c
219  */
220 void list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg)
221 {
222    VOLRES *vol;
223    POOL_MEM msg(PM_MESSAGE);
224    int len;
225
226    lock_volumes();
227    foreach_dlist(vol, vol_list) {
228       DEVICE *dev = vol->dev;
229       if (dev) {
230          len = Mmsg(msg, "%s on device %s\n", vol->vol_name, dev->print_name());
231          sendit(msg.c_str(), len, arg);
232          len = Mmsg(msg, "    Reader=%d writers=%d devres=%d volinuse=%d\n", 
233             dev->can_read()?1:0, dev->num_writers, dev->num_reserved(),   
234             vol->is_in_use());
235          sendit(msg.c_str(), len, arg);
236       } else {
237          len = Mmsg(msg, "%s no device. volinuse= %d\n", vol->vol_name, 
238             vol->is_in_use());
239          sendit(msg.c_str(), len, arg);
240       }
241    }
242    unlock_volumes();
243
244    lock_read_volumes();
245    foreach_dlist(vol, read_vol_list) {
246       len = Mmsg(msg, "%s read volume JobId=%d\n", vol->vol_name, 
247             vol->get_jobid());
248       sendit(msg.c_str(), len, arg);
249    }
250    unlock_read_volumes();
251
252 }
253
254 /*
255  * Create a Volume item to put in the Volume list
256  *   Ensure that the device points to it.
257  */
258 static VOLRES *new_vol_item(DCR *dcr, const char *VolumeName)
259 {
260    VOLRES *vol;
261    vol = (VOLRES *)malloc(sizeof(VOLRES));
262    memset(vol, 0, sizeof(VOLRES));
263    vol->vol_name = bstrdup(VolumeName);
264    if (dcr) {
265       vol->dev = dcr->dev;
266       Dmsg3(dbglvl, "new Vol=%s at %p dev=%s\n",
267             VolumeName, vol->vol_name, vol->dev->print_name());
268    }
269    return vol;
270 }
271
272 static void free_vol_item(VOLRES *vol)
273 {
274    DEVICE *dev = NULL;
275
276    free(vol->vol_name);
277    if (vol->dev) {
278       dev = vol->dev;
279    }
280    free(vol);
281    if (dev) {
282       dev->vol = NULL;
283    }
284 }
285
286 /*
287  * Put a new Volume entry in the Volume list. This
288  *  effectively reserves the volume so that it will
289  *  not be mounted again.
290  *
291  * If the device has any current volume associated with it,
292  *  and it is a different Volume, and the device is not busy,
293  *  we release the old Volume item and insert the new one.
294  * 
295  * It is assumed that the device is free and locked so that
296  *  we can change the device structure.
297  *
298  * Some details of the Volume list handling:
299  *
300  *  1. The Volume list entry must be attached to the drive (rather than 
301  *       attached to a job as it currently is. I.e. the drive that "owns" 
302  *       the volume (in use, mounted)
303  *       must point to the volume (still to be maintained in a list).
304  *
305  *  2. The Volume is entered in the list when a drive is reserved.  
306  *
307  *  3. When a drive is in use, the device code must appropriately update the
308  *      volume name as it changes (currently the list is static -- an entry is
309  *      removed when the Volume is no longer reserved, in use or mounted).  
310  *      The new code must keep the same list entry as long as the drive
311  *       has any volume associated with it but the volume name in the list
312  *       must be updated when the drive has a different volume mounted.
313  *
314  *  4. A job that has reserved a volume, can un-reserve the volume, and if the 
315  *      volume is not mounted, and not reserved, and not in use, it will be
316  *      removed from the list.
317  *
318  *  5. If a job wants to reserve a drive with a different Volume from the one on
319  *      the drive, it can re-use the drive for the new Volume.
320  *
321  *  6. If a job wants a Volume that is in a different drive, it can either use the
322  *      other drive or take the volume, only if the other drive is not in use or
323  *      not reserved.
324  *
325  *  One nice aspect of this is that the reserve use count and the writer use count 
326  *  already exist and are correctly programmed and will need no changes -- use 
327  *  counts are always very tricky.
328  *
329  *  The old code had a concept of "reserving" a Volume, but was changed 
330  *  to reserving and using a drive.  A volume is must be attached to (owned by) a 
331  *  drive and can move from drive to drive or be unused given certain specific 
332  *  conditions of the drive.  The key is that the drive must "own" the Volume.  
333  *  The old code had the job (dcr) owning the volume (more or less).  The job was
334  *  to change the insertion and removal of the volumes from the list to be based 
335  *  on the drive rather than the job.  
336  *
337  *  Return: VOLRES entry on success
338  *          NULL volume busy on another drive
339  */
340 VOLRES *reserve_volume(DCR *dcr, const char *VolumeName)
341 {
342    VOLRES *vol, *nvol;
343    DEVICE * volatile dev = dcr->dev;
344
345    ASSERT(dev != NULL);
346
347    Dmsg2(dbglvl, "enter reserve_volume=%s drive=%s\n", VolumeName, 
348       dcr->dev->print_name());
349    /* 
350     * We lock the reservations system here to ensure
351     *  when adding a new volume that no newly scheduled
352     *  job can reserve it.
353     */
354    lock_volumes();
355    debug_list_volumes("begin reserve_volume");
356    /* 
357     * First, remove any old volume attached to this device as it
358     *  is no longer used.
359     */
360    if (dev->vol) {
361       vol = dev->vol;
362       Dmsg4(dbglvl, "Vol attached=%s, newvol=%s volinuse=%d on %s\n",
363          vol->vol_name, VolumeName, vol->is_in_use(), dev->print_name());
364       /*
365        * Make sure we don't remove the current volume we are inserting
366        *  because it was probably inserted by another job, or it
367        *  is not being used and is marked as not reserved.
368        */
369       if (strcmp(vol->vol_name, VolumeName) == 0) {
370          Dmsg2(dbglvl, "=== set reserved vol=%s dev=%s\n", VolumeName,
371                vol->dev->print_name());
372          goto get_out;                  /* Volume already on this device */
373       } else {
374          /* Don't release a volume if it was reserved by someone other than us */
375          if (vol->is_in_use() && !dcr->reserved_volume) { 
376             Dmsg1(dbglvl, "Cannot free vol=%s. It is reserved.\n", vol->vol_name);
377             vol = NULL;                  /* vol in use */
378             goto get_out;
379          }
380          Dmsg2(dbglvl, "reserve_vol free vol=%s at %p\n", vol->vol_name, vol->vol_name);
381          free_volume(dev);
382          Dmsg0(50, "set_unload\n");
383          dev->set_unload();             /* have to unload current volume */
384          debug_list_volumes("reserve_vol free");
385       }
386    }
387
388    /* Create a new Volume entry */
389    nvol = new_vol_item(dcr, VolumeName);
390
391    /*
392     * Now try to insert the new Volume
393     */
394    vol = (VOLRES *)vol_list->binary_insert(nvol, my_compare);
395    if (vol != nvol) {
396       Dmsg2(dbglvl, "Found vol=%s dev-same=%d\n", vol->vol_name, dev==vol->dev);
397       /*
398        * At this point, a Volume with this name already is in the list,
399        *   so we simply release our new Volume entry. Note, this should
400        *   only happen if we are moving the volume from one drive to another.
401        */
402       Dmsg2(dbglvl, "reserve_vol free-tmp vol=%s at %p\n", 
403             vol->vol_name, vol->vol_name);
404       /*
405        * Clear dev pointer so that free_vol_item() doesn't 
406        *  take away our volume. 
407        */
408       nvol->dev = NULL;                  /* don't zap dev entry */
409       free_vol_item(nvol);
410
411       /*
412        * Check if we are trying to use the Volume on a different drive
413        *  dev      is our device
414        *  vol->dev is where the Volume we want is
415        */
416       if (dev != vol->dev) {
417          /* Caller wants to switch Volume to another device */
418          if (!vol->dev->is_busy() && !vol->is_swapping()) {
419             int32_t slot;
420             Dmsg3(dbglvl, "==== Swap vol=%s from dev=%s to %s\n", 
421                VolumeName, vol->dev->print_name(), dev->print_name());
422             free_volume(dev);            /* free any volume attached to our drive */
423             Dmsg0(50, "set_unload\n");
424             dev->set_unload();           /* Unload any volume that is on our drive */
425             dcr->dev = vol->dev;         /* temp point to other dev */
426             slot = get_autochanger_loaded_slot(dcr);  /* get slot on other drive */
427             dcr->dev = dev;              /* restore dev */
428             vol->set_slot(slot);         /* save slot */
429             vol->dev->set_unload();      /* unload the other drive */
430             vol->set_swapping();         /* swap from other drive */
431             dev->swap_dev = vol->dev;    /* remember to get this vol */
432             dev->set_load();             /* then reload on our drive */
433             vol->dev->vol = NULL;        /* remove volume from other drive */
434             vol->dev = dev;              /* point the Volume at our drive */
435             dev->vol = vol;              /* point our drive at the Volume */
436          } else {
437             Dmsg3(dbglvl, "==== Swap not possible Vol busy vol=%s from dev=%s to %s\n", 
438                VolumeName, vol->dev->print_name(), dev->print_name());
439             vol = NULL;                  /* device busy */
440             goto get_out;
441          }
442       } else {
443          dev->vol = vol;
444       }
445    } else {
446       dev->vol = vol;                    /* point to newly inserted volume */
447    }
448
449 get_out:
450    if (vol) {
451       Dmsg2(dbglvl, "=== set in_use. vol=%s dev=%s\n", vol->vol_name,
452             vol->dev->print_name());
453       vol->set_in_use();
454       dcr->reserved_volume = true;
455       bstrncpy(dcr->VolumeName, vol->vol_name, sizeof(dcr->VolumeName));
456    }
457    debug_list_volumes("end new volume");
458    unlock_volumes();
459    return vol;
460 }
461
462 /* 
463  * Switch from current device to given device  
464  *   (not yet used) 
465  */
466 #ifdef xxx
467 void switch_device(DCR *dcr, DEVICE *dev)
468 {
469    DCR save_dcr;
470
471    dev->dlock();
472    memcpy(&save_dcr, dcr, sizeof(save_dcr));
473    clean_device(dcr);                  /* clean up the dcr */
474
475    dcr->dev = dev;                     /* get new device pointer */
476    Jmsg(dcr->jcr, M_INFO, 0, _("Device switch. New device %s chosen.\n"),
477       dcr->dev->print_name());
478
479    bstrncpy(dcr->VolumeName, save_dcr.VolumeName, sizeof(dcr->VolumeName));
480    bstrncpy(dcr->media_type, save_dcr.media_type, sizeof(dcr->media_type));
481    dcr->VolCatInfo.Slot = save_dcr.VolCatInfo.Slot;
482    bstrncpy(dcr->pool_name, save_dcr.pool_name, sizeof(dcr->pool_name));
483    bstrncpy(dcr->pool_type, save_dcr.pool_type, sizeof(dcr->pool_type));
484    bstrncpy(dcr->dev_name, dev->dev_name, sizeof(dcr->dev_name));
485
486 // dcr->set_reserved();
487
488    dev->dunlock();
489 }
490 #endif
491
492 /*
493  * Search for a Volume name in the Volume list.
494  *
495  *  Returns: VOLRES entry on success
496  *           NULL if the Volume is not in the list
497  */
498 VOLRES *find_volume(const char *VolumeName) 
499 {
500    VOLRES vol, *fvol;
501    /* Do not lock reservations here */
502    lock_volumes();
503    vol.vol_name = bstrdup(VolumeName);
504    fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare);
505    free(vol.vol_name);
506    Dmsg2(dbglvl, "find_vol=%s found=%d\n", VolumeName, fvol!=NULL);
507    debug_list_volumes("find_volume");
508    unlock_volumes();
509    return fvol;
510 }
511
512 /*  
513  * Free a Volume from the Volume list if it is no longer used
514  *   Note, for tape drives we want to remember where the Volume
515  *   was when last used, so rather than free the volume entry,
516  *   we simply mark it "not reserved" so when the drive is really
517  *   needed for another volume, we can reuse it.
518  *
519  *  Returns: true if the Volume found and "removed" from the list
520  *           false if the Volume is not in the list or is in use
521  */
522 bool volume_unused(DCR *dcr)
523 {
524    DEVICE *dev = dcr->dev;
525
526    if (!dev->vol) {
527       Dmsg1(dbglvl, "vol_unused: no vol on %s\n", dev->print_name());
528       debug_list_volumes("null vol cannot unreserve_volume");
529       return false;
530    }
531    if (dev->vol->is_swapping()) {
532       Dmsg1(dbglvl, "vol_unused: vol being swapped on %s\n", dev->print_name());
533       Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name);
534       dev->vol->clear_in_use();
535       debug_list_volumes("swapping vol cannot free_volume");
536       return false;
537    }
538
539    /*  
540     * If this is a tape, we do not free the volume, rather we wait
541     *  until the autoloader unloads it, or until another tape is
542     *  explicitly read in this drive. This allows the SD to remember
543     *  where the tapes are or last were.
544     */
545    Dmsg4(dbglvl, "=== set not reserved vol=%s num_writers=%d dev_reserved=%d dev=%s\n",
546       dev->vol->vol_name, dev->num_writers, dev->num_reserved(), dev->print_name());
547    Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name);
548    dev->vol->clear_in_use();
549    if (dev->is_tape() || dev->is_autochanger()) {
550       return true;
551    } else {
552       /*
553        * Note, this frees the volume reservation entry, but the 
554        *   file descriptor remains open with the OS.
555        */
556       return free_volume(dev);
557    }
558 }
559
560 /*
561  * Unconditionally release the volume entry
562  */
563 bool free_volume(DEVICE *dev)
564 {
565    VOLRES *vol;
566
567    if (dev->vol == NULL) {
568       Dmsg1(dbglvl, "No vol on dev %s\n", dev->print_name());
569       return false;
570    }
571    lock_volumes();
572    vol = dev->vol;
573    /* Don't free a volume while it is being swapped */
574    if (!vol->is_swapping()) {
575       Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name);
576       dev->vol = NULL;
577       vol_list->remove(vol);
578       Dmsg2(dbglvl, "=== remove volume %s dev=%s\n", vol->vol_name, dev->print_name());
579       free_vol_item(vol);
580       debug_list_volumes("free_volume");
581    }
582    unlock_volumes();
583    return true;
584 }
585
586       
587 /* Create the Volume list */
588 void create_volume_lists()
589 {
590    VOLRES *vol = NULL;
591    if (vol_list == NULL) {
592       vol_list = New(dlist(vol, &vol->link));
593    }
594    if (read_vol_list == NULL) {
595       read_vol_list = New(dlist(vol, &vol->link));
596    }
597 }
598
599 /*
600  * Free normal append volumes list
601  */
602 static void free_volume_list()
603 {
604    VOLRES *vol;
605    if (vol_list) {
606       lock_volumes();
607       foreach_dlist(vol, vol_list) {
608          if (vol->dev) {
609             Dmsg2(dbglvl, "free vol_list Volume=%s dev=%s\n", vol->vol_name, vol->dev->print_name());
610          } else {
611             Dmsg1(dbglvl, "free vol_list Volume=%s No dev\n", vol->vol_name);
612          }
613          free(vol->vol_name);
614          vol->vol_name = NULL;
615       }
616       delete vol_list;
617       vol_list = NULL;
618       unlock_volumes();
619    }
620 }
621
622 /* Release all Volumes from the list */
623 void free_volume_lists()
624 {
625    VOLRES *vol;
626
627    free_volume_list();           /* normal append list */
628
629    if (read_vol_list) {
630       lock_read_volumes();
631       foreach_dlist(vol, read_vol_list) {
632          if (vol->dev) {
633             Dmsg2(dbglvl, "free read_vol_list Volume=%s dev=%s\n", vol->vol_name, vol->dev->print_name());
634          } else {
635             Dmsg1(dbglvl, "free read_vol_list Volume=%s No dev\n", vol->vol_name);
636          }
637          free(vol->vol_name);
638          vol->vol_name = NULL;
639       }
640       delete read_vol_list;
641       read_vol_list = NULL;
642       unlock_read_volumes();
643    }
644 }
645
646 bool DCR::can_i_use_volume()
647 {
648    bool rtn = true;
649    VOLRES *vol;
650
651    lock_volumes();
652    vol = find_volume(VolumeName);
653    if (!vol) {
654       Dmsg1(dbglvl, "Vol=%s not in use.\n", VolumeName);
655       goto get_out;                   /* vol not in list */
656    }
657    ASSERT(vol->dev != NULL);
658
659    if (dev == vol->dev) {        /* same device OK */
660       Dmsg1(dbglvl, "Vol=%s on same dev.\n", VolumeName);
661       goto get_out;
662    } else {
663       Dmsg3(dbglvl, "Vol=%s on %s we have %s\n", VolumeName,
664             vol->dev->print_name(), dev->print_name());
665    }
666    /* ***FIXME*** check this ... */
667    if (!vol->dev->is_busy()) {
668       Dmsg2(dbglvl, "Vol=%s dev=%s not busy.\n", VolumeName, vol->dev->print_name());
669       goto get_out;
670    } else {
671       Dmsg2(dbglvl, "Vol=%s dev=%s busy.\n", VolumeName, vol->dev->print_name());
672    }
673    Dmsg2(dbglvl, "Vol=%s in use by %s.\n", VolumeName, vol->dev->print_name());
674    rtn = false;
675
676 get_out:
677    unlock_volumes();
678    return rtn;
679
680 }
681
682 /*  
683  * Create a temporary copy of the volume list.  We do this,
684  *   to avoid having the volume list locked during the
685  *   call to reserve_device(), which would cause a deadlock.
686  * Note, we may want to add an update counter on the vol_list
687  *   so that if it is modified while we are traversing the copy
688  *   we can take note and act accordingly (probably redo the 
689  *   search at least a few times).
690  */
691 dlist *dup_vol_list(JCR *jcr)
692 {
693    dlist *temp_vol_list;
694    VOLRES *vol = NULL;
695
696    lock_volumes();
697    Dmsg0(dbglvl, "lock volumes\n");                           
698
699    Dmsg0(dbglvl, "duplicate vol list\n");
700    temp_vol_list = New(dlist(vol, &vol->link));
701    foreach_dlist(vol, vol_list) {
702       VOLRES *nvol;
703       VOLRES *tvol = (VOLRES *)malloc(sizeof(VOLRES));
704       memset(tvol, 0, sizeof(VOLRES));
705       tvol->vol_name = bstrdup(vol->vol_name);
706       tvol->dev = vol->dev;
707       nvol = (VOLRES *)temp_vol_list->binary_insert(tvol, my_compare);
708       if (tvol != nvol) {
709          tvol->dev = NULL;                   /* don't zap dev entry */
710          free_vol_item(tvol);
711          Pmsg0(000, "Logic error. Duplicating vol list hit duplicate.\n");
712          Jmsg(jcr, M_WARNING, 0, "Logic error. Duplicating vol list hit duplicate.\n");
713       }
714    }
715    Dmsg0(dbglvl, "unlock volumes\n");
716    unlock_volumes();
717    return temp_vol_list;
718 }
719
720 /*
721  * Free the specified temp list.
722  */
723 void free_temp_vol_list(dlist *temp_vol_list)
724 {
725    dlist *save_vol_list;
726    
727    lock_volumes();
728    save_vol_list = vol_list;
729    vol_list = temp_vol_list;
730    free_volume_list();                  /* release temp_vol_list */
731    vol_list = save_vol_list;
732    Dmsg0(dbglvl, "deleted temp vol list\n");
733    Dmsg0(dbglvl, "unlock volumes\n");
734    unlock_volumes();
735    debug_list_volumes("after free temp table");
736 }