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