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