1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2013 Free Software Foundation Europe e.V.
5 Copyright (C) 2015-2021 Bareos GmbH & Co. KG
6
7 This program is Free Software; you can redistribute it and/or
8 modify it under the terms of version three of the GNU Affero General Public
9 License as published by the Free Software Foundation and included
10 in the file LICENSE.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21 */
22 /*
23 * Kern Sibbald, MM
24 * Split from reserve.c October 2008
25 */
26 /**
27 * @file
28 * Volume management functions for Storage Daemon
29 */
30
31 #include "include/bareos.h"
32 #include "stored/stored.h"
33 #include "stored/stored_globals.h"
34 #include "stored/device_control_record.h"
35 #include "stored/autochanger.h"
36 #include "include/jcr.h"
37 #include "lib/berrno.h"
38
39 namespace storagedaemon {
40
41 const int debuglevel = 150;
42
43 static brwlock_t vol_list_lock;
44 static dlist* vol_list = NULL;
45 static dlist* read_vol_list = NULL;
46 static pthread_mutex_t read_vol_lock = PTHREAD_MUTEX_INITIALIZER;
47
48 /* Global static variables */
49 static int vol_list_lock_count = 0;
50 static int read_vol_list_lock_count = 0;
51
52 /* Forward referenced functions */
53 static void FreeVolItem(VolumeReservationItem* vol);
54 static void FreeReadVolItem(VolumeReservationItem* vol);
55 static VolumeReservationItem* new_vol_item(DeviceControlRecord* dcr,
56 const char* VolumeName);
57 static void DebugListVolumes(const char* imsg);
58
59 /**
60 * For append volumes the key is the VolumeName.
61 */
CompareByVolumename(void * item1,void * item2)62 static int CompareByVolumename(void* item1, void* item2)
63 {
64 VolumeReservationItem* vol1 = (VolumeReservationItem*)item1;
65 VolumeReservationItem* vol2 = (VolumeReservationItem*)item2;
66
67 ASSERT(vol1->vol_name);
68 ASSERT(vol2->vol_name);
69
70 return strcmp(vol1->vol_name, vol2->vol_name);
71 }
72
73 /**
74 * For read volumes the key is JobId, VolumeName.
75 */
ReadCompare(void * item1,void * item2)76 static int ReadCompare(void* item1, void* item2)
77 {
78 VolumeReservationItem* vol1 = (VolumeReservationItem*)item1;
79 VolumeReservationItem* vol2 = (VolumeReservationItem*)item2;
80
81 ASSERT(vol1->vol_name);
82 ASSERT(vol2->vol_name);
83
84 if (vol1->GetJobid() == vol2->GetJobid()) {
85 return strcmp(vol1->vol_name, vol2->vol_name);
86 }
87
88 if (vol1->GetJobid() < vol2->GetJobid()) { return -1; }
89
90 return 1;
91 }
92
IsVolListEmpty()93 bool IsVolListEmpty() { return vol_list->empty(); }
94
95 /**
96 * Initialized the main volume list. Note, we are using a recursive lock.
97 */
InitVolListLock()98 void InitVolListLock()
99 {
100 int errstat;
101
102 if ((errstat = RwlInit(&vol_list_lock, PRIO_SD_VOL_LIST)) != 0) {
103 BErrNo be;
104 Emsg1(M_ABORT, 0, _("Unable to initialize volume list lock. ERR=%s\n"),
105 be.bstrerror(errstat));
106 }
107 }
108
TermVolListLock()109 void TermVolListLock() { RwlDestroy(&vol_list_lock); }
110
111 /**
112 * This allows a given thread to recursively call to LockVolumes()
113 */
_lockVolumes(const char * file,int line)114 void _lockVolumes(const char* file, int line)
115 {
116 int errstat;
117
118 vol_list_lock_count++;
119 if ((errstat = RwlWritelock_p(&vol_list_lock, file, line)) != 0) {
120 BErrNo be;
121 Emsg2(M_ABORT, 0, "RwlWritelock failure. stat=%d: ERR=%s\n", errstat,
122 be.bstrerror(errstat));
123 }
124 }
125
_unLockVolumes()126 void _unLockVolumes()
127 {
128 int errstat;
129
130 vol_list_lock_count--;
131 if ((errstat = RwlWriteunlock(&vol_list_lock)) != 0) {
132 BErrNo be;
133 Emsg2(M_ABORT, 0, "RwlWriteunlock failure. stat=%d: ERR=%s\n", errstat,
134 be.bstrerror(errstat));
135 }
136 }
137
_lockReadVolumes(const char * file,int line)138 void _lockReadVolumes(const char* file, int line)
139 {
140 read_vol_list_lock_count++;
141 pthread_mutex_lock(&read_vol_lock);
142 }
143
_unLockReadVolumes()144 void _unLockReadVolumes()
145 {
146 read_vol_list_lock_count--;
147 pthread_mutex_unlock(&read_vol_lock);
148 }
149
150 /**
151 * Add a volume to the read list.
152 *
153 * Note, we use VolumeReservationItem because it simplifies the code
154 * even though, the only part of VolumeReservationItem that we need is
155 * the volume name. The same volume may be in the list
156 * multiple times, but each one is distinguished by the
157 * JobId. We use JobId, VolumeName as the key.
158 *
159 * We can get called multiple times for the same volume because
160 * when parsing the bsr, the volume name appears multiple times.
161 */
AddReadVolume(JobControlRecord * jcr,const char * VolumeName)162 void AddReadVolume(JobControlRecord* jcr, const char* VolumeName)
163 {
164 VolumeReservationItem *nvol, *vol;
165
166 nvol = new_vol_item(NULL, VolumeName);
167 nvol->SetJobid(jcr->JobId);
168 nvol->SetReading();
169 LockReadVolumes();
170 vol = (VolumeReservationItem*)read_vol_list->binary_insert(nvol, ReadCompare);
171 if (vol != nvol) {
172 FreeReadVolItem(nvol);
173 Dmsg2(debuglevel, "read_vol=%s JobId=%d already in list.\n", VolumeName,
174 jcr->JobId);
175 } else {
176 Dmsg2(debuglevel, "add_read_vol=%s JobId=%d\n", VolumeName, jcr->JobId);
177 }
178 UnlockReadVolumes();
179 }
180
181 /**
182 * Remove a given volume name from the read list.
183 */
RemoveReadVolume(JobControlRecord * jcr,const char * VolumeName)184 void RemoveReadVolume(JobControlRecord* jcr, const char* VolumeName)
185 {
186 VolumeReservationItem vol, *fvol;
187
188 LockReadVolumes();
189 vol.vol_name = strdup(VolumeName);
190 vol.SetJobid(jcr->JobId);
191
192 fvol
193 = (VolumeReservationItem*)read_vol_list->binary_search(&vol, ReadCompare);
194 free(vol.vol_name);
195
196 if (fvol) {
197 Dmsg3(debuglevel, "remove_read_vol=%s JobId=%d found=%d\n", VolumeName,
198 jcr->JobId, fvol != NULL);
199 }
200 if (fvol) {
201 read_vol_list->remove(fvol);
202 FreeReadVolItem(fvol);
203 }
204 UnlockReadVolumes();
205 // pthread_cond_broadcast(&wait_next_vol);
206 }
207
208 /**
209 * Search for a Volume name in the read Volume list.
210 *
211 * Returns: VolumeReservationItem entry on success
212 * NULL if the Volume is not in the list
213 */
find_read_volume(const char * VolumeName)214 static VolumeReservationItem* find_read_volume(const char* VolumeName)
215 {
216 VolumeReservationItem vol, *fvol;
217
218 if (read_vol_list->empty()) {
219 Dmsg0(debuglevel, "find_read_vol: read_vol_list empty.\n");
220 return NULL;
221 }
222
223 /*
224 * Do not lock reservations here
225 */
226 LockReadVolumes();
227 vol.vol_name = strdup(VolumeName);
228
229 /*
230 * Note, we do want a simple CompareByVolumename on volume name only here
231 */
232 fvol = (VolumeReservationItem*)read_vol_list->binary_search(
233 &vol, CompareByVolumename);
234 free(vol.vol_name);
235
236 Dmsg2(debuglevel, "find_read_vol=%s found=%d\n", VolumeName, fvol != NULL);
237 UnlockReadVolumes();
238
239 return fvol;
240 }
241
242 /**
243 * List Volumes -- this should be moved to status.c
244 */
245 enum
246 {
247 debug_lock = true,
248 debug_nolock = false
249 };
250
DebugListVolumes(const char * imsg)251 static void DebugListVolumes(const char* imsg)
252 {
253 VolumeReservationItem* vol;
254 PoolMem msg(PM_MESSAGE);
255
256 foreach_vol (vol) {
257 if (vol->dev) {
258 Mmsg(msg, "List %s: %s in_use=%d swap=%d on device %s\n", imsg,
259 vol->vol_name, vol->IsInUse(), vol->IsSwapping(),
260 vol->dev->print_name());
261 } else {
262 Mmsg(msg, "List %s: %s in_use=%d swap=%d no dev\n", imsg, vol->vol_name,
263 vol->IsInUse(), vol->IsSwapping());
264 }
265 Dmsg1(debuglevel, "%s", msg.c_str());
266 }
267 endeach_vol(vol);
268 }
269
270 /**
271 * Create a Volume item to put in the Volume list
272 * Ensure that the device points to it.
273 */
new_vol_item(DeviceControlRecord * dcr,const char * VolumeName)274 static VolumeReservationItem* new_vol_item(DeviceControlRecord* dcr,
275 const char* VolumeName)
276 {
277 VolumeReservationItem* vol;
278 VolumeReservationItem emptyVol;
279
280 vol = (VolumeReservationItem*)malloc(sizeof(VolumeReservationItem));
281 *vol = emptyVol;
282 vol->vol_name = strdup(VolumeName);
283 if (dcr) {
284 vol->dev = dcr->dev;
285 Dmsg3(debuglevel, "new Vol=%s at %p dev=%s\n", VolumeName, vol->vol_name,
286 vol->dev->print_name());
287 }
288 vol->InitMutex();
289 vol->IncUseCount();
290
291 return vol;
292 }
293
FreeVolItem(VolumeReservationItem * vol)294 static void FreeVolItem(VolumeReservationItem* vol)
295 {
296 Device* dev = NULL;
297
298 vol->DecUseCount();
299 vol->Lock();
300 if (vol->UseCount() > 0) {
301 vol->Unlock();
302 return;
303 }
304 vol->Unlock();
305 free(vol->vol_name);
306 if (vol->dev) { dev = vol->dev; }
307 vol->DestroyMutex();
308 free(vol);
309 if (dev) { dev->vol = NULL; }
310 }
311
FreeReadVolItem(VolumeReservationItem * vol)312 static void FreeReadVolItem(VolumeReservationItem* vol)
313 {
314 Device* dev = NULL;
315
316 vol->DecUseCount();
317 vol->Lock();
318 if (vol->UseCount() > 0) {
319 vol->Unlock();
320 return;
321 }
322 vol->Unlock();
323 free(vol->vol_name);
324 if (vol->dev) { dev = vol->dev; }
325 vol->DestroyMutex();
326 free(vol);
327 if (dev) { dev->vol = NULL; }
328 }
329
330 /**
331 * Put a new Volume entry in the Volume list. This
332 * effectively reserves the volume so that it will
333 * not be mounted again.
334 *
335 * If the device has any current volume associated with it,
336 * and it is a different Volume, and the device is not busy,
337 * we release the old Volume item and insert the new one.
338 *
339 * It is assumed that the device is free and locked so that
340 * we can change the device structure.
341 *
342 * Some details of the Volume list handling:
343 *
344 * 1. The Volume list entry is attached to the drive (rather than
345 * attached to a job as it was previously. I.e. the drive that "owns"
346 * the volume (in use, mounted)
347 * must point to the volume (still to be maintained in a list).
348 *
349 * 2. The Volume is entered in the list when a drive is reserved.
350 *
351 * 3. When a drive is in use, the device code must appropriately update the
352 * volume name as it changes.
353 *
354 * This code keeps the same list entry as long as the drive
355 * has any volume associated with it but the volume name in the list
356 * must be updated when the drive has a different volume mounted.
357 *
358 * 4. A job that has reserved a volume, can un-reserve the volume, and if the
359 * volume is not mounted, and not reserved, and not in use, it will be
360 * removed from the list.
361 *
362 * 5. If a job wants to reserve a drive with a different Volume from the one on
363 * the drive, it can re-use the drive for the new Volume.
364 *
365 * 6. If a job wants a Volume that is in a different drive, it can either use
366 * the other drive or take the volume, only if the other drive is not in use or
367 * not reserved.
368 *
369 * One nice aspect of this is that the reserve use count and the writer use
370 * count already exist and are correctly programmed and will need no changes --
371 * use counts are always very tricky.
372 *
373 * The old code had a concept of "reserving" a Volume, but was changed
374 * to reserving and using a drive. A volume is must be attached to (owned by)
375 * a drive and can move from drive to drive or be unused given certain specific
376 * conditions of the drive. The key is that the drive must "own" the Volume.
377 *
378 * Return: VolumeReservationItem entry on success
379 * NULL volume busy on another drive
380 */
reserve_volume(DeviceControlRecord * dcr,const char * VolumeName)381 VolumeReservationItem* reserve_volume(DeviceControlRecord* dcr,
382 const char* VolumeName)
383 {
384 VolumeReservationItem *vol, *nvol;
385 Device* volatile dev = dcr->dev;
386
387 if (JobCanceled(dcr->jcr)) { return NULL; }
388 ASSERT(dev != NULL);
389
390 Dmsg2(debuglevel, "enter reserve_volume=%s drive=%s\n", VolumeName,
391 dcr->dev->print_name());
392
393 /*
394 * If aquiring a volume for writing it may not be on the read volume list.
395 */
396 if (me->filedevice_concurrent_read && dcr->IsWriting()
397 && find_read_volume(VolumeName)) {
398 Mmsg(dcr->jcr->errmsg,
399 _("Could not reserve volume \"%s\" for append, because it is read by "
400 "another Job.\n"),
401 dev->VolHdr.VolumeName);
402 return NULL;
403 }
404
405 /*
406 * We lock the reservations system here to ensure when adding a new volume
407 * that no newly scheduled job can reserve it.
408 */
409 LockVolumes();
410 if (debug_level >= debuglevel) { DebugListVolumes("begin reserve_volume"); }
411
412 /*
413 * First, remove any old volume attached to this device as it is no longer
414 * used.
415 */
416 if (dev->vol) {
417 vol = dev->vol;
418 Dmsg4(debuglevel, "Vol attached=%s, newvol=%s volinuse=%d on %s\n",
419 vol->vol_name, VolumeName, vol->IsInUse(), dev->print_name());
420 /*
421 * Make sure we don't remove the current volume we are inserting
422 * because it was probably inserted by another job, or it
423 * is not being used and is marked as not reserved.
424 */
425 if (bstrcmp(vol->vol_name, VolumeName)) {
426 Dmsg2(debuglevel, "=== set reserved vol=%s dev=%s\n", VolumeName,
427 vol->dev->print_name());
428 goto get_out; /* Volume already on this device */
429 } else {
430 /*
431 * Don't release a volume if it was reserved by someone other than us
432 */
433 if (vol->IsInUse() && !dcr->reserved_volume) {
434 Dmsg1(debuglevel, "Cannot free vol=%s. It is reserved.\n",
435 vol->vol_name);
436 vol = NULL; /* vol in use */
437 goto get_out;
438 }
439 Dmsg2(debuglevel, "reserve_vol free vol=%s at %p\n", vol->vol_name,
440 vol->vol_name);
441
442 /*
443 * If old Volume is still mounted, must unload it
444 */
445 if (bstrcmp(vol->vol_name, dev->VolHdr.VolumeName)) {
446 Dmsg0(50, "SetUnload\n");
447 dev->SetUnload(); /* have to unload current volume */
448 }
449 FreeVolume(dev); /* Release old volume entry */
450
451 if (debug_level >= debuglevel) { DebugListVolumes("reserve_vol free"); }
452 }
453 }
454
455 /*
456 * Create a new Volume entry
457 */
458 nvol = new_vol_item(dcr, VolumeName);
459
460 /*
461 * See if this is a request for reading a file type device which can be
462 * accesses by multiple readers at once without disturbing each other.
463 */
464 if (me->filedevice_concurrent_read && !dcr->IsWriting() && dev->IsFile()) {
465 nvol->SetJobid(dcr->jcr->JobId);
466 nvol->SetReading();
467 vol = nvol;
468 dev->vol = vol;
469
470 /*
471 * Read volumes on file based devices are not inserted into the write volume
472 * list.
473 */
474 goto get_out;
475 } else {
476 /*
477 * Now try to insert the new Volume
478 */
479 vol = (VolumeReservationItem*)vol_list->binary_insert(nvol,
480 CompareByVolumename);
481 }
482
483 if (vol != nvol) {
484 Dmsg2(debuglevel, "Found vol=%s dev-same=%d\n", vol->vol_name,
485 dev == vol->dev);
486
487 /*
488 * At this point, a Volume with this name already is in the list,
489 * so we simply release our new Volume entry. Note, this should
490 * only happen if we are moving the volume from one drive to another.
491 */
492 Dmsg2(debuglevel, "reserve_vol free-tmp vol=%s at %p\n", vol->vol_name,
493 vol->vol_name);
494
495 /*
496 * Clear dev pointer so that FreeVolItem() doesn't take away our volume.
497 */
498 nvol->dev = NULL; /* don't zap dev entry */
499 FreeVolItem(nvol);
500
501 if (vol->dev) {
502 Dmsg2(debuglevel, "dev=%s vol->dev=%s\n", dev->print_name(),
503 vol->dev->print_name());
504 }
505
506 /*
507 * Check if we are trying to use the Volume on a different drive dev is our
508 * device vol->dev is where the Volume we want is
509 */
510 if (dev != vol->dev) {
511 /*
512 * Caller wants to switch Volume to another device
513 */
514
515 // should be different devices with different names
516 if (bstrcmp(dev->print_name(), vol->dev->print_name())) {
517 // names are same
518 Dmsg1(100, "device pointers are different but have same name %s\n",
519 dev->print_name());
520 }
521
522 if (!vol->dev->IsBusy() && !vol->IsSwapping()) {
523 slot_number_t slot;
524
525 Dmsg3(debuglevel, "==== Swap vol=%s from dev=%s to %s\n", VolumeName,
526 vol->dev->print_name(), dev->print_name());
527 FreeVolume(dev); /* free any volume attached to our drive */
528 Dmsg1(50, "SetUnload dev=%s\n", dev->print_name());
529 dev->SetUnload(); /* Unload any volume that is on our drive */
530 dcr->SetDev(vol->dev); /* temp point to other dev */
531 slot = GetAutochangerLoadedSlot(dcr); /* get slot on other drive */
532 dcr->SetDev(dev); /* restore dev */
533 vol->SetSlotNumber(slot); /* save slot */
534 vol->dev->SetUnload(); /* unload the other drive */
535 vol->SetSwapping(); /* swap from other drive */
536 dev->swap_dev = vol->dev; /* remember to get this vol */
537 dev->SetLoad(); /* then reload on our drive */
538 vol->dev->vol = NULL; /* remove volume from other drive */
539 vol->dev = dev; /* point the Volume at our drive */
540 dev->vol = vol; /* point our drive at the Volume */
541 } else {
542 Jmsg7(dcr->jcr, M_WARNING, 0,
543 "Need volume from other drive, but swap not possible. "
544 "Status: read=%d num_writers=%d num_reserve=%d swap=%d "
545 "vol=%s from dev=%s to %s\n",
546 vol->dev->CanRead(), vol->dev->num_writers,
547 vol->dev->NumReserved(), vol->IsSwapping(), VolumeName,
548 vol->dev->print_name(), dev->print_name());
549 if (vol->IsSwapping() && dev->swap_dev) {
550 Dmsg3(debuglevel, "Swap failed vol=%s from=%s to dev=%s\n",
551 vol->vol_name, dev->swap_dev->print_name(), dev->print_name());
552 } else {
553 Dmsg3(debuglevel, "Swap failed vol=%s from=%p to dev=%s\n",
554 vol->vol_name, dev->swap_dev, dev->print_name());
555 }
556
557 if (debug_level >= debuglevel) { DebugListVolumes("failed swap"); }
558
559 vol = NULL; /* device busy */
560 goto get_out;
561 }
562 } else {
563 dev->vol = vol;
564 }
565 } else {
566 dev->vol = vol; /* point to newly inserted volume */
567 }
568
569 get_out:
570 if (vol) {
571 Dmsg2(debuglevel, "=== set in_use. vol=%s dev=%s\n", vol->vol_name,
572 vol->dev->print_name());
573 vol->SetInUse();
574 dcr->reserved_volume = true;
575 bstrncpy(dcr->VolumeName, vol->vol_name, sizeof(dcr->VolumeName));
576 }
577
578 if (debug_level >= debuglevel) { DebugListVolumes("end new volume"); }
579
580 UnlockVolumes();
581 return vol;
582 }
583
584 /**
585 * Start walk of vol chain
586 * The proper way to walk the vol chain is:
587 *
588 * VolumeReservationItem *vol;
589 * foreach_vol(vol) {
590 * ...
591 * }
592 * endeach_vol(vol);
593 *
594 * It is possible to leave out the endeach_vol(vol), but in that case,
595 * the last vol referenced must be explicitly released with:
596 *
597 * FreeVolItem(vol);
598 */
vol_walk_start()599 VolumeReservationItem* vol_walk_start()
600 {
601 VolumeReservationItem* vol;
602 LockVolumes();
603 vol = (VolumeReservationItem*)vol_list->first();
604 if (vol) {
605 vol->IncUseCount();
606 Dmsg2(debuglevel, "Inc walk_start UseCount=%d volname=%s\n",
607 vol->UseCount(), vol->vol_name);
608 }
609 UnlockVolumes();
610
611 return vol;
612 }
613
614 /**
615 * Get next vol from chain, and release current one
616 */
VolWalkNext(VolumeReservationItem * prev_vol)617 VolumeReservationItem* VolWalkNext(VolumeReservationItem* prev_vol)
618 {
619 VolumeReservationItem* vol;
620
621 LockVolumes();
622 vol = (VolumeReservationItem*)vol_list->next(prev_vol);
623 if (vol) {
624 vol->IncUseCount();
625 Dmsg2(debuglevel, "Inc walk_next UseCount=%d volname=%s\n", vol->UseCount(),
626 vol->vol_name);
627 }
628 if (prev_vol) { FreeVolItem(prev_vol); }
629 UnlockVolumes();
630
631 return vol;
632 }
633
634 /**
635 * Release last vol referenced
636 */
VolWalkEnd(VolumeReservationItem * vol)637 void VolWalkEnd(VolumeReservationItem* vol)
638 {
639 if (vol) {
640 LockVolumes();
641 Dmsg2(debuglevel, "Free walk_end UseCount=%d volname=%s\n", vol->UseCount(),
642 vol->vol_name);
643 FreeVolItem(vol);
644 UnlockVolumes();
645 }
646 }
647
648 /*
649 * Start walk of vol chain
650 * The proper way to walk the vol chain is:
651 *
652 * VolumeReservationItem *vol;
653 * foreach_read_vol(vol) {
654 * ...
655 * }
656 * endeach_read_vol(vol);
657 *
658 * It is possible to leave out the endeach_read_vol(vol), but in that case,
659 * the last vol referenced must be explicitly released with:
660 *
661 * FreeReadVolItem(vol);
662 */
read_vol_walk_start()663 VolumeReservationItem* read_vol_walk_start()
664 {
665 VolumeReservationItem* vol;
666 LockReadVolumes();
667 vol = (VolumeReservationItem*)read_vol_list->first();
668 if (vol) {
669 vol->IncUseCount();
670 Dmsg2(debuglevel, "Inc walk_start UseCount=%d volname=%s\n",
671 vol->UseCount(), vol->vol_name);
672 }
673 UnlockReadVolumes();
674
675 return vol;
676 }
677
678 /*
679 * Get next vol from chain, and release current one
680 */
ReadVolWalkNext(VolumeReservationItem * prev_vol)681 VolumeReservationItem* ReadVolWalkNext(VolumeReservationItem* prev_vol)
682 {
683 VolumeReservationItem* vol;
684
685 LockReadVolumes();
686 vol = (VolumeReservationItem*)read_vol_list->next(prev_vol);
687 if (vol) {
688 vol->IncUseCount();
689 Dmsg2(debuglevel, "Inc walk_next UseCount=%d volname=%s\n", vol->UseCount(),
690 vol->vol_name);
691 }
692 if (prev_vol) { FreeReadVolItem(prev_vol); }
693 UnlockReadVolumes();
694
695 return vol;
696 }
697
698 /*
699 * Release last vol referenced
700 */
ReadVolWalkEnd(VolumeReservationItem * vol)701 void ReadVolWalkEnd(VolumeReservationItem* vol)
702 {
703 if (vol) {
704 LockReadVolumes();
705 Dmsg2(debuglevel, "Free walk_end UseCount=%d volname=%s\n", vol->UseCount(),
706 vol->vol_name);
707 FreeReadVolItem(vol);
708 UnlockReadVolumes();
709 }
710 }
711
712 /**
713 * Search for a Volume name in the Volume list.
714 *
715 * Returns: VolumeReservationItem entry on success
716 * NULL if the Volume is not in the list
717 */
find_volume(const char * VolumeName)718 static VolumeReservationItem* find_volume(const char* VolumeName)
719 {
720 VolumeReservationItem vol, *fvol;
721
722 if (vol_list->empty()) { return NULL; }
723 /* Do not lock reservations here */
724 LockVolumes();
725 vol.vol_name = strdup(VolumeName);
726 fvol = (VolumeReservationItem*)vol_list->binary_search(&vol,
727 CompareByVolumename);
728 free(vol.vol_name);
729 Dmsg2(debuglevel, "find_vol=%s found=%d\n", VolumeName, fvol != NULL);
730
731 if (debug_level >= debuglevel) { DebugListVolumes("find_volume"); }
732
733 UnlockVolumes();
734 return fvol;
735 }
736
737 /**
738 * Free a Volume from the Volume list if it is no longer used
739 * Note, for tape drives we want to remember where the Volume
740 * was when last used, so rather than free the volume entry,
741 * we simply mark it "not reserved" so when the drive is really
742 * needed for another volume, we can reuse it.
743 *
744 * Returns: true if the Volume found and "removed" from the list
745 * false if the Volume is not in the list or is in use
746 */
VolumeUnused(DeviceControlRecord * dcr)747 bool VolumeUnused(DeviceControlRecord* dcr)
748 {
749 Device* dev = dcr->dev;
750
751 if (!dev->vol) {
752 Dmsg1(debuglevel, "vol_unused: no vol on %s\n", dev->print_name());
753 if (debug_level >= debuglevel) {
754 DebugListVolumes("null vol cannot unreserve_volume");
755 }
756
757 return false;
758 }
759
760 Dmsg1(debuglevel, "=== clear in_use vol=%s\n", dev->vol->vol_name);
761 dev->vol->ClearInUse();
762
763 if (dev->vol->IsSwapping()) {
764 Dmsg1(debuglevel, "vol_unused: vol being swapped on %s\n",
765 dev->print_name());
766
767 if (debug_level >= debuglevel) {
768 DebugListVolumes("swapping vol cannot FreeVolume");
769 }
770 return false;
771 }
772
773 /*
774 * If this is a tape, we do not free the volume, rather we wait
775 * until the autoloader unloads it, or until another tape is
776 * explicitly read in this drive. This allows the SD to remember
777 * where the tapes are or last were.
778 */
779 Dmsg4(debuglevel,
780 "=== set not reserved vol=%s num_writers=%d dev_reserved=%d dev=%s\n",
781 dev->vol->vol_name, dev->num_writers, dev->NumReserved(),
782 dev->print_name());
783 if (dev->IsTape() || dev->AttachedToAutochanger()) {
784 return true;
785 } else {
786 /*
787 * Note, this frees the volume reservation entry, but the file descriptor
788 * remains open with the OS.
789 */
790 return FreeVolume(dev);
791 }
792 }
793
794 /**
795 * Unconditionally release the volume entry
796 */
FreeVolume(Device * dev)797 bool FreeVolume(Device* dev)
798 {
799 VolumeReservationItem* vol;
800
801 LockVolumes();
802 vol = dev->vol;
803 if (vol == NULL) {
804 Dmsg1(debuglevel, "No vol on dev %s\n", dev->print_name());
805 UnlockVolumes();
806 return false;
807 }
808
809 /*
810 * Don't free a volume while it is being swapped
811 */
812 if (!vol->IsSwapping()) {
813 Dmsg1(debuglevel, "=== clear in_use vol=%s\n", vol->vol_name);
814 dev->vol = NULL;
815
816 /*
817 * Volume is on write volume list if one of the folling is applicable:
818 * - The volume is written to.
819 * - Config option filedevice_concurrent_read is not on.
820 * - The device is not of type File.
821 */
822 if (vol->IsWriting() || !me->filedevice_concurrent_read || !dev->IsFile()) {
823 vol_list->remove(vol);
824 }
825 Dmsg2(debuglevel, "=== remove volume %s dev=%s\n", vol->vol_name,
826 dev->print_name());
827 FreeVolItem(vol);
828
829 if (debug_level >= debuglevel) { DebugListVolumes("FreeVolume"); }
830 } else {
831 Dmsg1(debuglevel, "=== cannot clear swapping vol=%s\n", vol->vol_name);
832 }
833 UnlockVolumes();
834 // pthread_cond_broadcast(&wait_next_vol);
835
836 return true;
837 }
838
839 /**
840 * Create the Volume list
841 */
CreateVolumeLists()842 void CreateVolumeLists()
843 {
844 VolumeReservationItem* vol = NULL;
845 if (vol_list == NULL) { vol_list = new dlist(vol, &vol->link); }
846 if (read_vol_list == NULL) { read_vol_list = new dlist(vol, &vol->link); }
847 }
848
849 /**
850 * Free normal append volumes list
851 */
FreeVolumeList(const char * what,dlist * vollist)852 static inline void FreeVolumeList(const char* what, dlist* vollist)
853 {
854 VolumeReservationItem* vol;
855
856 foreach_dlist (vol, vollist) {
857 if (vol->dev) {
858 Dmsg3(debuglevel, "free %s Volume=%s dev=%s\n", what, vol->vol_name,
859 vol->dev->print_name());
860 } else {
861 Dmsg2(debuglevel, "free %s Volume=%s No dev\n", what, vol->vol_name);
862 }
863 free(vol->vol_name);
864 vol->vol_name = NULL;
865 vol->DestroyMutex();
866 }
867 }
868
869 /**
870 * Release all Volumes from the list
871 */
FreeVolumeLists()872 void FreeVolumeLists()
873 {
874 if (vol_list) {
875 LockVolumes();
876 FreeVolumeList("vol_list", vol_list);
877 delete vol_list;
878 vol_list = NULL;
879 UnlockVolumes();
880 }
881
882 if (read_vol_list) {
883 LockReadVolumes();
884 FreeVolumeList("read_vol_list", read_vol_list);
885 delete read_vol_list;
886 read_vol_list = NULL;
887 UnlockReadVolumes();
888 }
889 }
890
891 /**
892 * Determine if caller can write on volume
893 */
Can_i_write_volume()894 bool DeviceControlRecord::Can_i_write_volume()
895 {
896 VolumeReservationItem* vol;
897
898 vol = find_read_volume(VolumeName);
899 if (vol) {
900 Dmsg1(100, "Found in read list; cannot write vol=%s\n", VolumeName);
901 return false;
902 }
903
904 return Can_i_use_volume();
905 }
906
907 /**
908 * Determine if caller can read or write volume
909 */
Can_i_use_volume()910 bool DeviceControlRecord::Can_i_use_volume()
911 {
912 bool rtn = true;
913 VolumeReservationItem* vol;
914
915 if (JobCanceled(jcr)) { return false; }
916 LockVolumes();
917 vol = find_volume(VolumeName);
918 if (!vol) {
919 Dmsg1(debuglevel, "Vol=%s not in use.\n", VolumeName);
920 goto get_out; /* vol not in list */
921 }
922 ASSERT(vol->dev != NULL);
923
924 if (dev == vol->dev) { /* same device OK */
925 Dmsg1(debuglevel, "Vol=%s on same dev.\n", VolumeName);
926 goto get_out;
927 } else {
928 Dmsg3(debuglevel, "Vol=%s on %s we have %s\n", VolumeName,
929 vol->dev->print_name(), dev->print_name());
930 }
931 /* ***FIXME*** check this ... */
932 if (!vol->dev->IsBusy()) {
933 Dmsg2(debuglevel, "Vol=%s dev=%s not busy.\n", VolumeName,
934 vol->dev->print_name());
935 goto get_out;
936 } else {
937 Dmsg2(debuglevel, "Vol=%s dev=%s busy.\n", VolumeName,
938 vol->dev->print_name());
939 }
940 Dmsg2(debuglevel, "Vol=%s in use by %s.\n", VolumeName,
941 vol->dev->print_name());
942 rtn = false;
943
944 get_out:
945 UnlockVolumes();
946 return rtn;
947 }
948
949 /**
950 * Create a temporary copy of the volume list. We do this,
951 * to avoid having the volume list locked during the
952 * call to ReserveDevice(), which would cause a deadlock.
953 *
954 * Note, we may want to add an update counter on the vol_list
955 * so that if it is modified while we are traversing the copy
956 * we can take note and act accordingly (probably redo the
957 * search at least a few times).
958 */
dup_vol_list(JobControlRecord * jcr)959 dlist* dup_vol_list(JobControlRecord* jcr)
960 {
961 dlist* temp_vol_list;
962 VolumeReservationItem* vol = NULL;
963
964 Dmsg0(debuglevel, "lock volumes\n");
965
966 Dmsg0(debuglevel, "duplicate vol list\n");
967 temp_vol_list = new dlist(vol, &vol->link);
968 foreach_vol (vol) {
969 VolumeReservationItem *nvol, *tvol;
970
971 tvol = new_vol_item(NULL, vol->vol_name);
972 tvol->dev = vol->dev;
973 nvol = (VolumeReservationItem*)temp_vol_list->binary_insert(
974 tvol, CompareByVolumename);
975 if (tvol != nvol) {
976 tvol->dev = NULL; /* don't zap dev entry */
977 FreeVolItem(tvol);
978 Pmsg0(000, "Logic error. Duplicating vol list hit duplicate.\n");
979 Jmsg(jcr, M_WARNING, 0,
980 "Logic error. Duplicating vol list hit duplicate.\n");
981 }
982 }
983 endeach_vol(vol);
984 Dmsg0(debuglevel, "unlock volumes\n");
985
986 return temp_vol_list;
987 }
988
989 /**
990 * Free the specified temp list.
991 */
FreeTempVolList(dlist * temp_vol_list)992 void FreeTempVolList(dlist* temp_vol_list)
993 {
994 FreeVolumeList("temp_vol_list", temp_vol_list);
995 delete temp_vol_list;
996 }
997
998 } /* namespace storagedaemon */
999