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