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