/*
Bacula® - The Network Backup Solution
- Copyright (C) 2002-2008 Free Software Foundation Europe e.V.
+ Copyright (C) 2002-2012 Free Software Foundation Europe e.V.
The main author of Bacula is Kern Sibbald, with contributions from
many others, a complete list can be found in the file AUTHORS.
This program is Free Software; you can redistribute it and/or
- modify it under the terms of version two of the GNU General Public
+ modify it under the terms of version three of the GNU Affero General Public
License as published by the Free Software Foundation and included
in the file LICENSE.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
- You should have received a copy of the GNU General Public License
+ You should have received a copy of the GNU Affero General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*
* Kern Sibbald, August MMII
*
- * Version $Id$
*/
#include "bacula.h" /* pull in global headers */
#include "stored.h" /* pull in Storage Deamon headers */
-
enum {
try_next_vol = 1,
try_read_vol,
* This routine returns a 0 only if it is REALLY
* impossible to get the requested Volume.
*
+ * This routine is entered with the device blocked, but not
+ * locked.
+ *
*/
bool DCR::mount_next_write_volume()
{
int retry = 0;
bool ask = false, recycle, autochanger;
- bool do_find = true;
int mode;
DCR *dcr = this;
dev->print_name());
init_device_wait_timers(dcr);
- lock_volumes();
/*
* Attempt to mount the next volume. If something non-fatal goes
* wrong, we come back here to re-try (new op messages, re-read
* Volume, ...)
*/
+ lock_volumes();
+
mount_next_vol:
Dmsg1(150, "mount_next_vol retry=%d\n", retry);
/* Ignore retry if this is poll request */
- if (!dev->poll && retry++ > 4) {
+ if (retry++ > 4) {
/* Last ditch effort before giving up, force operator to respond */
VolCatInfo.Slot = 0;
unlock_volumes();
}
recycle = false;
- if (retry >= 2) {
- do_find = false;
- }
-
if (dev->must_unload()) {
ask = true; /* ask operator to mount tape */
- do_find = true; /* re-find a volume after unload */
}
- do_swapping(true /*writing*/);
+ do_unload();
+ do_swapping(true /*is_writing*/);
+ do_load(true /*is_writing*/);
- if (do_find && !find_a_volume()) {
- goto no_lock_bail_out;
+ if (!find_a_volume()) {
+ goto bail_out;
}
if (job_canceled(jcr)) {
goto bail_out;
}
Dmsg3(150, "After find_next_append. Vol=%s Slot=%d Parts=%d\n",
- VolCatInfo.VolCatName, VolCatInfo.Slot, VolCatInfo.VolCatParts);
+ getVolCatName(), VolCatInfo.Slot, VolCatInfo.VolCatParts);
/*
* Get next volume and ready it for append
* and move the tape to the end of data.
*
*/
+ dcr->setVolCatInfo(false); /* out of date when Vols unlocked */
if (autoload_device(dcr, true/*writing*/, NULL) > 0) {
autochanger = true;
ask = false;
if (ask) {
unlock_volumes();
+ dcr->setVolCatInfo(false); /* out of date when Vols unlocked */
if (!dir_ask_sysop_to_mount_volume(dcr, ST_APPEND)) {
Dmsg0(150, "Error return ask_sysop ...\n");
goto no_lock_bail_out;
if (dev->poll && dev->has_cap(CAP_CLOSEONPOLL)) {
dev->close();
+ free_volume(dev);
}
/* Ensure the device is open */
mode = OPEN_READ_WRITE;
}
/* Try autolabel if enabled */
- if (dev->open(dcr, mode) < 0) {
+ if (!dev->open(dcr, mode)) {
try_autolabel(false); /* try to create a new volume label */
}
- while (dev->open(dcr, mode) < 0) {
+ while (!dev->open(dcr, mode)) {
Dmsg1(150, "open_device failed: ERR=%s\n", dev->bstrerror());
if ((dev->is_file() && dev->is_removable()) || dev->is_dvd()) {
bool ok = true;
}
}
if (ok && dev->scan_dir_for_volume(dcr)) {
- if (dev->open(dcr, mode) >= 0) {
+ if (dev->open(dcr, mode)) {
break; /* got a valid volume */
}
}
if (try_autolabel(false) == try_read_vol) {
break; /* created a new volume label */
}
+ Jmsg3(jcr, M_WARNING, 0, _("Open device %s Volume \"%s\" failed: ERR=%s\n"),
+ dev->print_name(), dcr->VolumeName, dev->bstrerror());
Dmsg0(50, "set_unload\n");
dev->set_unload(); /* force ask sysop */
ask = true;
case check_ok:
break;
}
+ /*
+ * Check that volcatinfo is good
+ */
+ if (!dev->haveVolCatInfo()) {
+ Dmsg0(210, "Do not have volcatinfo\n");
+ if (!find_a_volume()) {
+ goto mount_next_vol;
+ }
+ dev->VolCatInfo = VolCatInfo; /* structure assignment */
+ }
/*
* See if we have a fresh tape or a tape with data.
*/
recycle = strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0;
if (dev->VolHdr.LabelType == PRE_LABEL || recycle) {
- if (!rewrite_volume_label(dcr, recycle)) {
+ if (!dcr->rewrite_volume_label(recycle)) {
mark_volume_in_error();
goto mount_next_vol;
}
* we need to position to the end of the volume, since we are
* just now putting it into append mode.
*/
- Dmsg0(200, "Device previously written, moving to end of data\n");
+ Dmsg1(100, "Device previously written, moving to end of data. Expect %lld bytes\n",
+ dev->VolCatInfo.VolCatBytes);
Jmsg(jcr, M_INFO, 0, _("Volume \"%s\" previously written, moving to end of data.\n"),
VolumeName);
if (!dev->eod(dcr)) {
+ Dmsg2(050, "Unable to position to end of data on device %s: ERR=%s\n",
+ dev->print_name(), dev->bstrerror());
Jmsg(jcr, M_ERROR, 0, _("Unable to position to end of data on device %s: ERR=%s\n"),
dev->print_name(), dev->bstrerror());
mark_volume_in_error();
goto mount_next_vol;
}
if (!is_eod_valid()) {
- Dmsg0(150, "goto mount_next_vol\n");
+ Dmsg0(100, "goto mount_next_vol\n");
goto mount_next_vol;
}
* This routine is meant to be called once the first pass
* to ensure that we have a candidate volume to mount.
* Otherwise, we ask the sysop to created one.
+ * Note, the the Volumes are locked on entry and exit.
*/
-bool DCR::find_a_volume()
+bool DCR::find_a_volume()
{
DCR *dcr = this;
+
if (!is_suitable_volume_mounted()) {
bool have_vol = false;
/* Do we have a candidate volume? */
while (!dir_find_next_appendable_volume(dcr)) {
Dmsg0(200, "not dir_find_next\n");
if (job_canceled(jcr)) {
- unlock_volumes();
return false;
}
unlock_volumes();
if (!dir_ask_sysop_to_create_appendable_volume(dcr)) {
+ lock_volumes();
return false;
}
lock_volumes();
+ if (job_canceled(jcr)) {
+ return false;
+ }
Dmsg0(150, "Again dir_find_next_append...\n");
}
}
}
- return true;
+ if (dcr->haveVolCatInfo()) {
+ return true;
+ }
+ return dir_get_volume_info(dcr, GET_VOL_INFO_FOR_WRITE);
}
int DCR::check_volume_label(bool &ask, bool &autochanger)
Dmsg2(150, "Want dirVol=%s dirStat=%s\n", VolumeName,
VolCatInfo.VolCatStatus);
+
/*
* At this point, dev->VolCatInfo has what is in the drive, if anything,
* and dcr->VolCatInfo has what the Director wants.
Jmsg2(jcr, M_WARNING, 0, _("Could not reserve volume %s on %s\n"),
dev->VolHdr.VolumeName, dev->print_name());
ask = true;
+ dev->setVolCatInfo(false);
+ setVolCatInfo(false);
goto check_next_volume;
}
break; /* got a Volume */
case try_default:
break;
}
-
/* NOTE! Fall-through wanted. */
case VOL_NO_MEDIA:
default:
/* Needed, so the medium can be changed */
if (dev->requires_mount()) {
dev->close();
+ free_volume(dev);
}
goto check_next_volume;
}
return check_ok;
check_next_volume:
+ dev->setVolCatInfo(false);
+ setVolCatInfo(false);
return check_next_vol;
check_bail_out:
return dir_get_volume_info(this, GET_VOL_INFO_FOR_WRITE);
}
-void DCR::do_swapping(bool is_writing)
+bool DCR::do_unload()
{
if (dev->must_unload()) {
Dmsg1(100, "must_unload release %s\n", dev->print_name());
release_volume();
}
+ return false;
+}
+
+bool DCR::do_load(bool is_writing)
+{
+ if (dev->must_load()) {
+ Dmsg1(100, "Must load %s\n", dev->print_name());
+ if (autoload_device(this, is_writing, NULL) > 0) {
+ dev->clear_load();
+ return true;
+ }
+ return false;
+ }
+ return true;
+}
+
+void DCR::do_swapping(bool is_writing)
+{
/*
* See if we are asked to swap the Volume from another device
* if so, unload the other device here, and attach the
Dmsg1(100, "=== set in_use vol=%s\n", dev->vol->vol_name);
dev->vol->set_in_use();
dev->VolHdr.VolumeName[0] = 0; /* don't yet have right Volume */
+ } else {
+ Dmsg1(100, "No vol on dev=%s\n", dev->print_name());
}
- dev->swap_dev = NULL;
- }
- if (dev->must_load()) {
- Dmsg1(100, "Must load %s\n", dev->print_name());
- if (autoload_device(this, is_writing, NULL) > 0) {
- dev->clear_load();
+ if (dev->swap_dev->vol) {
+ Dmsg2(100, "Vol=%s on dev=%s\n", dev->swap_dev->vol->vol_name,
+ dev->swap_dev->print_name());
}
+ Dmsg2(100, "Set swap_dev=NULL for dev=%s swap_dev=%s\n",
+ dev->print_name(), dev->swap_dev->print_name());
+ dev->swap_dev = NULL;
+ } else {
+ Dmsg0(100, "No swap_dev set\n");
}
}
if (dev->VolCatInfo.VolCatFiles == dev->get_file()) {
Jmsg(jcr, M_INFO, 0, _("Ready to append to end of Volume \"%s\" at file=%d.\n"),
VolumeName, dev->get_file());
+ } else if (dev->get_file() > dev->VolCatInfo.VolCatFiles) {
+ Jmsg(jcr, M_WARNING, 0, _("For Volume \"%s\":\n"
+ "The number of files mismatch! Volume=%u Catalog=%u\n"
+ "Correcting Catalog\n"),
+ VolumeName, dev->get_file(), dev->VolCatInfo.VolCatFiles);
+ dev->VolCatInfo.VolCatFiles = dev->get_file();
+ dev->VolCatInfo.VolCatBlocks = dev->get_block_num();
+ if (!dir_update_volume_info(this, false, true)) {
+ Jmsg(jcr, M_WARNING, 0, _("Error updating Catalog\n"));
+ mark_volume_in_error();
+ return false;
+ }
} else {
Jmsg(jcr, M_ERROR, 0, _("Bacula cannot write on tape Volume \"%s\" because:\n"
"The number of files mismatch! Volume=%u Catalog=%u\n"),
Jmsg(jcr, M_INFO, 0, _("Ready to append to end of Volume \"%s\""
" size=%s\n"), VolumeName,
edit_uint64(dev->VolCatInfo.VolCatBytes, ed1));
+ } else if ((uint64_t)pos > dev->VolCatInfo.VolCatBytes) {
+ Jmsg(jcr, M_WARNING, 0, _("For Volume \"%s\":\n"
+ "The sizes do not match! Volume=%s Catalog=%s\n"
+ "Correcting Catalog\n"),
+ VolumeName, edit_uint64(pos, ed1),
+ edit_uint64(dev->VolCatInfo.VolCatBytes, ed2));
+ dev->VolCatInfo.VolCatBytes = (uint64_t)pos;
+ dev->VolCatInfo.VolCatFiles = (uint32_t)(pos >> 32);
+ if (!dir_update_volume_info(this, false, true)) {
+ Jmsg(jcr, M_WARNING, 0, _("Error updating Catalog\n"));
+ mark_volume_in_error();
+ return false;
+ }
} else {
- Jmsg(jcr, M_ERROR, 0, _("Bacula cannot write on disk Volume \"%s\" because: "
+ Mmsg(jcr->errmsg, _("Bacula cannot write on disk Volume \"%s\" because: "
"The sizes do not match! Volume=%s Catalog=%s\n"),
VolumeName,
edit_uint64(pos, ed1),
edit_uint64(dev->VolCatInfo.VolCatBytes, ed2));
+ Jmsg(jcr, M_ERROR, 0, jcr->errmsg);
+ Dmsg0(050, jcr->errmsg);
mark_volume_in_error();
return false;
}
{
Jmsg(jcr, M_ERROR, 0, _("Autochanger Volume \"%s\" not found in slot %d.\n"
" Setting InChanger to zero in catalog.\n"),
- VolCatInfo.VolCatName, VolCatInfo.Slot);
+ getVolCatName(), VolCatInfo.Slot);
dev->VolCatInfo = VolCatInfo; /* structure assignment */
VolCatInfo.InChanger = false;
dev->VolCatInfo.InChanger = false;
if (dev->is_open()) {
dev->offline_or_rewind();
}
-// Dmsg0(50, "set_unload\n");
-// dev->set_unload();
Dmsg0(190, "release_volume\n");
}
* End Of Tape -- mount next Volume (if another specified)
*/
if (jcr->NumReadVolumes > 1 && jcr->CurReadVolume < jcr->NumReadVolumes) {
+ dev->Lock();
dev->close();
+ dev->set_read();
+ dcr->set_reserved();
+ dev->Unlock();
if (!acquire_device_for_read(dcr)) {
Jmsg2(jcr, M_FATAL, 0, _("Cannot open Dev=%s, Vol=%s\n"), dev->print_name(),
dcr->VolumeName);