From b56d7b6ad8ed88f350867e8860c38ecdc92eef80 Mon Sep 17 00:00:00 2001 From: Kern Sibbald Date: Wed, 16 Apr 2008 09:49:47 +0000 Subject: [PATCH] Create 2.2.10 by backporting trunk SD reservation changes git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/branches/Branch-2.2@6831 91ce42f0-d328-0410-95d8-f526ca767f89 --- bacula/ChangeLog | 52 ++++ bacula/ReleaseNotes | 54 ++++ bacula/src/dird/jobq.c | 16 +- bacula/src/stored/acquire.c | 178 ++++------- bacula/src/stored/ansi_label.c | 4 +- bacula/src/stored/append.c | 6 +- bacula/src/stored/askdir.c | 29 +- bacula/src/stored/autochanger.c | 52 ++-- bacula/src/stored/bscan.c | 2 +- bacula/src/stored/dev.c | 72 ++++- bacula/src/stored/dev.h | 96 +++++- bacula/src/stored/device.c | 8 +- bacula/src/stored/dircmd.c | 2 +- bacula/src/stored/dvd.c | 2 +- bacula/src/stored/label.c | 19 +- bacula/src/stored/lock.c | 2 +- bacula/src/stored/mount.c | 526 ++++++++++++++++++-------------- bacula/src/stored/protos.h | 5 +- bacula/src/stored/reserve.c | 295 +++++++++--------- bacula/src/stored/scan.c | 20 +- bacula/src/stored/stored.h | 2 +- bacula/src/stored/stored_conf.h | 17 +- bacula/src/version.h | 8 +- bacula/technotes-2.1 | 52 ++++ 24 files changed, 934 insertions(+), 585 deletions(-) diff --git a/bacula/ChangeLog b/bacula/ChangeLog index 812fe5d38e..7fe65ab0b4 100644 --- a/bacula/ChangeLog +++ b/bacula/ChangeLog @@ -1,6 +1,58 @@ Technical notes on version 2.2.x General: +16Apr08 +kes Permit multiple simultaneous restores -- experiment. +15Apr08 +kes Backport development stream SD reservation system changes. + See below: +=== +16Apr08 +kes Move final volume swapping code to DCR method and + call it from acquire_for_read(). +kes When wrong volume is mounted during read, unload_autochanger. +14Apr08 +kes Stop searching for Volumes in SD askdir if DIR returns the + same volume name twice in a row. +13Apr08 +kes Tweak the swap Volume from one drive to another code so that + it now seems to work. +kes Resolve several problems with recycling that broke the new + code (these problems are in 2.2.9 as well). +kes Remove the volume lock when calling the sysop code. +kes Don't mark a reserved volume with no writers as unused otherwise + jobs fail. +12Apr08 +kes Rework class structures for VOLRES, DCR, and DEVICE to make + the method names a bit more logical, and for more logically + handling the responsibilities. +11Apr08 +kes Remove redundant code in terminating the scheduler that just + causes a seg fault in many cases. +kes Cleanup releasing a volume and make it a DCR method. +kes Improve algorithm for detecting pre-reserved volume and + swapping volumes. +10Apr08 +kes Tweak volume swapping code so it works. +kes Correct name overloading in mount.c +kes Replace released flag in VOLRES with reserved and add access + methods for the VOLRES class. +kes Prevent volume from being released while being swapped. +kes Refactor parts of stored/mount.c +09Apr08 +ebl Add sanity checks for VolWriteTime and VolReadTime +ebl Take care of bad clock changes while computing VolWriteTime + and VolReadTime. This should fix or limit #1066 +kes Correct error string numbers in dird/catreq.c +kes Restructure reserving, acquiring, and mounting volumes. Calls to + autochanger are deferred for mount.c -- simplifes the code. +08Apr08 +kes Do not prune any running job. It just fails the job. +kes Lock the volumes when changing dev->reserved_device and marking + the volume unused otherwise the device can get reserved by + another job before the volume is released, thus blocking it. +=== + Beta Release Version 2.2.9-b7 14Apr08 kes Close bat console windows first to eliminate error message diff --git a/bacula/ReleaseNotes b/bacula/ReleaseNotes index 6d8110cba1..777ada9428 100644 --- a/bacula/ReleaseNotes +++ b/bacula/ReleaseNotes @@ -10,6 +10,60 @@ use some of the new features that affect the FD. In other words, you should not have to upgrade all your File daemons when you upgrade. There is no database upgrade needed from version 2.0.x to 2.2.0. +Version 2.2.10 is primarily a Storage daemon reservations system + enhancement to 2.2.9-beta +16Apr08 +kes Permit multiple simultaneous restores -- experiment. +15Apr08 +kes Backport development stream SD reservation system changes. + See below: +=== +16Apr08 +kes Move final volume swapping code to DCR method and + call it from acquire_for_read(). +kes When wrong volume is mounted during read, unload_autochanger. +14Apr08 +kes Stop searching for Volumes in SD askdir if DIR returns the + same volume name twice in a row. +13Apr08 +kes Tweak the swap Volume from one drive to another code so that + it now seems to work. +kes Resolve several problems with recycling that broke the new + code (these problems are in 2.2.9 as well). +kes Remove the volume lock when calling the sysop code. +kes Don't mark a reserved volume with no writers as unused otherwise + jobs fail. +12Apr08 +kes Rework class structures for VOLRES, DCR, and DEVICE to make + the method names a bit more logical, and for more logically + handling the responsibilities. +11Apr08 +kes Remove redundant code in terminating the scheduler that just + causes a seg fault in many cases. +kes Cleanup releasing a volume and make it a DCR method. +kes Improve algorithm for detecting pre-reserved volume and + swapping volumes. +10Apr08 +kes Tweak volume swapping code so it works. +kes Correct name overloading in mount.c +kes Replace released flag in VOLRES with reserved and add access + methods for the VOLRES class. +kes Prevent volume from being released while being swapped. +kes Refactor parts of stored/mount.c +09Apr08 +ebl Add sanity checks for VolWriteTime and VolReadTime +ebl Take care of bad clock changes while computing VolWriteTime + and VolReadTime. This should fix or limit #1066 +kes Correct error string numbers in dird/catreq.c +kes Restructure reserving, acquiring, and mounting volumes. Calls to + autochanger are deferred for mount.c -- simplifes the code. +08Apr08 +kes Do not prune any running job. It just fails the job. +kes Lock the volumes when changing dev->reserved_device and marking + the volume unused otherwise the device can get reserved by + another job before the volume is released, thus blocking it. +=== + Version 2.2.9 is a bug fix to version 2.2.8: - Close bat console windows first to eliminate error message from the notifier. diff --git a/bacula/src/dird/jobq.c b/bacula/src/dird/jobq.c index 462ed0b853..0f3df014b1 100644 --- a/bacula/src/dird/jobq.c +++ b/bacula/src/dird/jobq.c @@ -683,12 +683,8 @@ static bool acquire_resources(JCR *jcr) jcr->acquired_resource_locks = false; if (jcr->rstore) { Dmsg1(200, "Rstore=%s\n", jcr->rstore->name()); - /* - * Let only one Restore/Verify job run at a time regardless - * of MaxConcurrentjobs. - */ - if (jcr->rstore->NumConcurrentJobs == 0) { - jcr->rstore->NumConcurrentJobs = 1; + if (jcr->rstore->NumConcurrentJobs < jcr->rstore->MaxConcurrentJobs) { + jcr->rstore->NumConcurrentJobs++; Dmsg0(200, "Set rncj=1\n"); } else { Dmsg1(200, "Fail rncj=%d\n", jcr->rstore->NumConcurrentJobs); @@ -700,7 +696,7 @@ static bool acquire_resources(JCR *jcr) if (jcr->wstore) { Dmsg1(200, "Wstore=%s\n", jcr->wstore->name()); if (jcr->rstore == jcr->wstore) { /* deadlock */ - jcr->rstore->NumConcurrentJobs = 0; /* back out rstore */ + jcr->rstore->NumConcurrentJobs--; /* back out rstore */ Jmsg(jcr, M_FATAL, 0, _("Job canceled. Attempt to read and write same device.\n" " Read storage \"%s\" (From %s) -- Write storage \"%s\" (From %s)\n"), jcr->rstore->name(), jcr->rstore_source, jcr->wstore->name(), jcr->wstore_source); @@ -716,7 +712,7 @@ static bool acquire_resources(JCR *jcr) jcr->wstore->NumConcurrentJobs++; Dmsg1(200, "Inc wncj=%d\n", jcr->wstore->NumConcurrentJobs); } else if (jcr->rstore) { - jcr->rstore->NumConcurrentJobs = 0; /* back out rstore */ + jcr->rstore->NumConcurrentJobs--; /* back out rstore */ Dmsg1(200, "Fail wncj=%d\n", jcr->wstore->NumConcurrentJobs); skip_this_jcr = true; } else { @@ -738,7 +734,7 @@ static bool acquire_resources(JCR *jcr) Dmsg1(200, "Dec wncj=%d\n", jcr->wstore->NumConcurrentJobs); } if (jcr->rstore) { - jcr->rstore->NumConcurrentJobs = 0; + jcr->rstore->NumConcurrentJobs--; Dmsg1(200, "Dec rncj=%d\n", jcr->rstore->NumConcurrentJobs); } set_jcr_job_status(jcr, JS_WaitClientRes); @@ -753,7 +749,7 @@ static bool acquire_resources(JCR *jcr) Dmsg1(200, "Dec wncj=%d\n", jcr->wstore->NumConcurrentJobs); } if (jcr->rstore) { - jcr->rstore->NumConcurrentJobs = 0; + jcr->rstore->NumConcurrentJobs--; Dmsg1(200, "Dec rncj=%d\n", jcr->rstore->NumConcurrentJobs); } jcr->client->NumConcurrentJobs--; diff --git a/bacula/src/stored/acquire.c b/bacula/src/stored/acquire.c index 482fcca93e..5733a4b803 100644 --- a/bacula/src/stored/acquire.c +++ b/bacula/src/stored/acquire.c @@ -38,7 +38,7 @@ /* Forward referenced functions */ static void attach_dcr_to_dev(DCR *dcr); -static bool is_suitable_volume_mounted(DCR *dcr); +static bool is_tape_position_ok(JCR *jcr, DEVICE *dev); /********************************************************************* @@ -192,6 +192,8 @@ bool acquire_device_for_read(DCR *dcr) goto get_out; /* error return */ } + dcr->do_swapping(); + autoload_device(dcr, 0, NULL); /* @@ -236,6 +238,7 @@ bool acquire_device_for_read(DCR *dcr) } else { bstrncpy(dev->BadVolName, dev->VolHdr.VolumeName, sizeof(dev->BadVolName)); } + unload_autochanger(dcr, -1); /* Fall through */ default: Jmsg1(jcr, M_WARNING, 0, "%s", jcr->errmsg); @@ -286,11 +289,7 @@ default_path: get_out: dev->dlock(); - if (dcr && dcr->reserved_device) { - dev->reserved_device--; - Dmsg2(50, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name()); - dcr->reserved_device = false; - } + dcr->clear_reserved(); /* * Normally we are blocked, but in at least one error case above * we are not blocked because we unsuccessfully tried changing @@ -315,11 +314,9 @@ get_out: */ DCR *acquire_device_for_append(DCR *dcr) { - bool do_mount = false; - bool release = false; - bool have_vol; DEVICE *dev = dcr->dev; JCR *jcr = dcr->jcr; + bool ok = false; init_device_wait_timers(dcr); @@ -340,87 +337,29 @@ DCR *acquire_device_for_append(DCR *dcr) * have_vol defines whether or not mount_next_write_volume should * ask the Director again about what Volume to use. */ - have_vol = is_suitable_volume_mounted(dcr); - if (dev->can_append()) { + if (dev->can_append() && dcr->is_suitable_volume_mounted() && + strcmp(dcr->VolCatInfo.VolCatStatus, "Recycle") != 0) { Dmsg0(190, "device already in append.\n"); /* - * Device already in append mode - * - * Check if we have the right Volume mounted - * OK if current volume info OK - * OK if next volume matches current volume - * otherwise mount desired volume obtained from - * dir_find_next_appendable_volume - * dev->VolHdr.VolumeName is what is in the drive - * dcr->VolumeName is what we pass into the routines, or - * get back from the subroutines. + * At this point, the correct tape is already mounted, so + * we do not need to do mount_next_write_volume(), unless + * we need to recycle the tape. */ - if (!have_vol && - !(dir_find_next_appendable_volume(dcr) && - strcmp(dev->VolHdr.VolumeName, dcr->VolumeName) == 0)) { /* wrong tape mounted */ - /* Wrong tape mounted, release it, then fall through to get correct one */ - Dmsg3(50, "Wrong tape mounted. Wanted:%s, got:%s, dev=%s release and try mount.\n", - dcr->VolumeName, dev->VolHdr.VolumeName, dev->print_name()); - /* Do not release if no Volume in drive */ - if (dev->VolHdr.VolumeName[0]) { - release = true; - } - do_mount = true; - } else { - /* - * At this point, the correct tape is already mounted, so - * we do not need to do mount_next_write_volume(), unless - * we need to recycle the tape. - */ - do_mount = strcmp(dcr->VolCatInfo.VolCatStatus, "Recycle") == 0; - Dmsg2(190, "jid=%u Correct tape mounted. recycle=%d\n", - (uint32_t)jcr->JobId, do_mount); - if (dev->num_writers == 0) { - memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo)); - } - - /* - * Insanity check - * - * Check to see if the tape position as defined by the OS is - * the same as our concept. If it is not, we bail out, because - * it means the user has probably manually rewound the tape. - * Note, we check only if num_writers == 0, but this code will - * also work fine for any number of writers. If num_writers > 0, - * we probably should cancel all jobs using this device, or - * perhaps even abort the SD, or at a minimum, mark the tape - * in error. Another strategy with num_writers == 0, would be - * to rewind the tape and do a new eod() request. - */ - if (dev->is_tape() && dev->num_writers == 0) { - int32_t file = dev->get_os_tape_file(); - if (file >= 0 && file != (int32_t)dev->get_file()) { - Jmsg(jcr, M_FATAL, 0, _("Invalid tape position on volume \"%s\"" - " on device %s. Expected %d, got %d\n"), - dev->VolHdr.VolumeName, dev->print_name(), dev->get_file(), file); - goto get_out; - } - } - } + if (dev->num_writers == 0) { + memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo)); + } + if (!is_tape_position_ok(jcr, dev)) { + goto get_out; + } } else { - /* Not already in append mode, so mount the device */ - Dmsg2(190, "jid=%u Not in append mode, try mount have_vol=%d\n", - (uint32_t)jcr->JobId, have_vol); - - ASSERT(dev->num_writers == 0); - do_mount = true; - } - - if (do_mount || !have_vol) { Dmsg1(190, "jid=%u Do mount_next_write_vol\n", (uint32_t)jcr->JobId); - bool mounted = mount_next_write_volume(dcr, have_vol, release); - if (!mounted) { + if (!dcr->mount_next_write_volume()) { if (!job_canceled(jcr)) { /* Reduce "noise" -- don't print if job canceled */ Jmsg(jcr, M_FATAL, 0, _("Could not ready device %s for append.\n"), dev->print_name()); - Dmsg2(200, "jid=%u Could not ready device %s for append.\n", - (uint32_t)jcr->JobId, dev->print_name()); + Dmsg1(200, "Could not ready device %s for append.\n", + dev->print_name()); } goto get_out; } @@ -433,44 +372,43 @@ DCR *acquire_device_for_append(DCR *dcr) } dev->VolCatInfo.VolCatJobs++; /* increment number of jobs on vol */ dir_update_volume_info(dcr, false, false); /* send Volume info to Director */ - dev->dlock(); - if (dcr->reserved_device) { - dev->reserved_device--; - Dmsg3(100, "jid=%u Dec reserve=%d dev=%s\n", (uint32_t)jcr->JobId, - dev->reserved_device, dev->print_name()); - dcr->reserved_device = false; - } - dev->dunblock(DEV_LOCKED); - return dcr; + ok = true; -/* - * Error return - */ get_out: dev->dlock(); - if (dcr->reserved_device) { - dev->reserved_device--; - Dmsg3(100, "jid=%u Dec reserve=%d dev=%s\n", (uint32_t)jcr->JobId, - dev->reserved_device, dev->print_name()); - dcr->reserved_device = false; - } + dcr->clear_reserved(); dev->dunblock(DEV_LOCKED); - return NULL; + return ok ? dcr : NULL; } - -static bool is_suitable_volume_mounted(DCR *dcr) +/* + * Insanity check + * + * Check to see if the tape position as defined by the OS is + * the same as our concept. If it is not, we bail out, because + * it means the user has probably manually rewound the tape. + * Note, we check only if num_writers == 0, but this code will + * also work fine for any number of writers. If num_writers > 0, + * we probably should cancel all jobs using this device, or + * perhaps even abort the SD, or at a minimum, mark the tape + * in error. Another strategy with num_writers == 0, would be + * to rewind the tape and do a new eod() request. + */ +static bool is_tape_position_ok(JCR *jcr, DEVICE *dev) { - DEVICE *dev = dcr->dev; - - /* Volume mounted? */ - if (dev->VolHdr.VolumeName[0] == 0) { - return false; /* no */ + if (dev->is_tape() && dev->num_writers == 0) { + int32_t file = dev->get_os_tape_file(); + if (file >= 0 && file != (int32_t)dev->get_file()) { + Jmsg(jcr, M_FATAL, 0, _("Invalid tape position on volume \"%s\"" + " on device %s. Expected %d, got %d\n"), + dev->VolHdr.VolumeName, dev->print_name(), dev->get_file(), file); + return false; + } } - bstrncpy(dcr->VolumeName, dev->VolHdr.VolumeName, sizeof(dcr->VolumeName)); - return dir_get_volume_info(dcr, GET_VOL_INFO_FOR_WRITE); + return true; } + /* * This job is done, so release the device. From a Unix standpoint, * the device remains open. @@ -489,14 +427,11 @@ bool release_device(DCR *dcr) if (!dcr->is_dev_locked()) { dev->r_dlock(); } + lock_volumes(); Dmsg2(100, "release_device device %s is %s\n", dev->print_name(), dev->is_tape()?"tape":"disk"); /* if device is reserved, job never started, so release the reserve here */ - if (dcr->reserved_device) { - dev->reserved_device--; - Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name()); - dcr->reserved_device = false; - } + dcr->clear_reserved(); if (dev->can_read()) { dev->clear_read(); /* clear read bit */ @@ -514,7 +449,8 @@ bool release_device(DCR *dcr) dev->num_writers--; Dmsg1(100, "There are %d writers in release_device\n", dev->num_writers); if (dev->is_labeled()) { - Dmsg0(100, "dir_create_jobmedia_record. Release\n"); + Dmsg2(200, "dir_create_jobmedia. Release vol=%s dev=%s\n", + dev->VolCatInfo.VolCatName, dev->print_name()); if (!dev->at_weot() && !dir_create_jobmedia_record(dcr)) { Jmsg(jcr, M_FATAL, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"), dcr->VolCatInfo.VolCatName, jcr->Job); @@ -524,14 +460,15 @@ bool release_device(DCR *dcr) dev->weof(1); write_ansi_ibm_labels(dcr, ANSI_EOF_LABEL, dev->VolHdr.VolumeName); } - if (!dev->is_busy()) { /* if no more writers */ - volume_unused(dcr); /* we obviously are not using the volume */ - } if (!dev->at_weot()) { dev->VolCatInfo.VolCatFiles = dev->file; /* set number of files */ /* Note! do volume update before close, which zaps VolCatInfo */ - Dmsg0(100, "dir_update_vol_info. Release0\n"); dir_update_volume_info(dcr, false, false); /* send Volume info to Director */ + Dmsg2(200, "dir_update_vol_info. Release vol=%s dev=%s\n", + dev->VolCatInfo.VolCatName, dev->print_name()); + } + if (!dev->is_busy()) { /* if not being used */ + volume_unused(dcr); /* we obviously are not using the volume */ } } @@ -543,6 +480,7 @@ bool release_device(DCR *dcr) */ volume_unused(dcr); } + unlock_volumes(); /* If no writers, close if file or !CAP_ALWAYS_OPEN */ if (dev->num_writers == 0 && (!dev->is_tape() || !dev->has_cap(CAP_ALWAYSOPEN))) { @@ -706,7 +644,7 @@ void detach_dcr_from_dev(DCR *dcr) /* Detach this dcr only if attached */ if (dcr->attached_to_dev && dev) { dev->dlock(); - unreserve_device(dcr); + dcr->unreserve_device(); dcr->dev->attached_dcrs->remove(dcr); /* detach dcr from device */ dcr->attached_to_dev = false; // remove_dcr_from_dcrs(dcr); /* remove dcr from jcr list */ diff --git a/bacula/src/stored/ansi_label.c b/bacula/src/stored/ansi_label.c index e8978ec7bc..237064b6d9 100644 --- a/bacula/src/stored/ansi_label.c +++ b/bacula/src/stored/ansi_label.c @@ -65,7 +65,7 @@ static bool same_label_names(char *bacula_name, char *ansi_name); */ int read_ansi_ibm_label(DCR *dcr) { - DEVICE *dev = dcr->dev; + DEVICE * volatile dev = dcr->dev; JCR *jcr = dcr->jcr; char label[80]; /* tape label */ int stat, i; @@ -147,7 +147,9 @@ int read_ansi_ibm_label(DCR *dcr) *q++ = *p++; } *q = 0; + Dmsg0(100, "Call reserve_volume\n"); reserve_volume(dcr, dev->VolHdr.VolumeName); + dev = dcr->dev; /* may have changed in reserve_volume */ Dmsg2(100, "Wanted ANSI Vol %s got %6s\n", VolName, dev->VolHdr.VolumeName); Mmsg2(jcr->errmsg, _("Wanted ANSI Volume \"%s\" got \"%s\"\n"), VolName, dev->VolHdr.VolumeName); return VOL_NAME_ERROR; diff --git a/bacula/src/stored/append.c b/bacula/src/stored/append.c index 972b14bb4c..382dd2d556 100644 --- a/bacula/src/stored/append.c +++ b/bacula/src/stored/append.c @@ -1,7 +1,7 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2000-2007 Free Software Foundation Europe e.V. + Copyright (C) 2000-2008 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. @@ -70,7 +70,7 @@ bool do_append_data(JCR *jcr) return false; } - Dmsg1(100, "Start append data. res=%d\n", dev->reserved_device); + Dmsg1(100, "Start append data. res=%d\n", dev->num_reserved()); memset(&rec, 0, sizeof(rec)); @@ -296,6 +296,7 @@ bool do_append_data(JCR *jcr) } if (dev->VolCatInfo.VolCatName[0] == 0) { Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n")); + Dmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n")); } Dmsg0(90, "back from write_end_session_label()\n"); /* Flush out final partial block of this session */ @@ -307,6 +308,7 @@ bool do_append_data(JCR *jcr) } if (dev->VolCatInfo.VolCatName[0] == 0) { Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n")); + Dmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n")); } } diff --git a/bacula/src/stored/askdir.c b/bacula/src/stored/askdir.c index c5adcdbe5f..a5ad1a6976 100644 --- a/bacula/src/stored/askdir.c +++ b/bacula/src/stored/askdir.c @@ -242,7 +242,7 @@ bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing) * Returns: true on success dcr->VolumeName is volume * reserve_volume() called on Volume name * false on failure dcr->VolumeName[0] == 0 - * also sets dcr->volume_in_use if at least one + * also sets dcr->found_in_use if at least one * in use volume was found. * * Volume information returned in dcr @@ -253,19 +253,21 @@ bool dir_find_next_appendable_volume(DCR *dcr) JCR *jcr = dcr->jcr; BSOCK *dir = jcr->dir_bsock; bool rtn; + char lastVolume[MAX_NAME_LENGTH]; Dmsg2(200, "dir_find_next_appendable_volume: reserved=%d Vol=%s\n", - dcr->reserved_device, dcr->VolumeName); + dcr->is_reserved(), dcr->VolumeName); /* - * Try the fourty oldest or most available volumes. Note, + * Try the twenty oldest or most available volumes. Note, * the most available could already be mounted on another * drive, so we continue looking for a not in use Volume. */ lock_volumes(); P(vol_info_mutex); - dcr->volume_in_use = false; - for (int vol_index=1; vol_index < 40; vol_index++) { + dcr->clear_found_in_use(); + lastVolume[0] = 0; + for (int vol_index=1; vol_index < 20; vol_index++) { bash_spaces(dcr->media_type); bash_spaces(dcr->pool_name); dir->fsend(Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type); @@ -273,18 +275,27 @@ bool dir_find_next_appendable_volume(DCR *dcr) unbash_spaces(dcr->pool_name); Dmsg1(100, ">dird %s", dir->msg); if (do_get_volume_info(dcr)) { - if (!is_volume_in_use(dcr)) { - Dmsg0(400, "dir_find_next_appendable_volume return true\n"); + /* Give up if we get the same volume name twice */ + if (lastVolume[0] && strcmp(lastVolume, dcr->VolumeName) == 0) { + Dmsg1(100, "Got same vol = %s\n", lastVolume); + break; + } + bstrncpy(lastVolume, dcr->VolumeName, sizeof(lastVolume)); + if (dcr->can_i_use_volume()) { + Dmsg1(100, "Call reserve_volume. Vol=%s\n", dcr->VolumeName); if (reserve_volume(dcr, dcr->VolumeName) == 0) { Dmsg2(100, "Could not reserve volume %s on %s\n", dcr->VolumeName, dcr->dev->print_name()); continue; } + Dmsg1(100, "dir_find_next_appendable_volume return true. vol=%s\n", + dcr->VolumeName); rtn = true; goto get_out; } else { Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName); - dcr->volume_in_use = true; + /* If volume is not usable, it is in use by someone else */ + dcr->set_found_in_use(); continue; } } @@ -399,7 +410,7 @@ bool dir_create_jobmedia_record(DCR *dcr) dcr->Copy, dcr->Stripe, edit_uint64(dcr->VolMediaId, ed1)); Dmsg1(100, ">dird %s", dir->msg); - if (bnet_recv(dir) <= 0) { + if (dir->recv() <= 0) { Dmsg0(190, "create_jobmedia error bnet_recv\n"); Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"), dir->bstrerror()); diff --git a/bacula/src/stored/autochanger.c b/bacula/src/stored/autochanger.c index 866ede8343..ce895fe18e 100644 --- a/bacula/src/stored/autochanger.c +++ b/bacula/src/stored/autochanger.c @@ -1,7 +1,7 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2002-2007 Free Software Foundation Europe e.V. + Copyright (C) 2002-2008 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. @@ -111,7 +111,7 @@ bool init_autochangers() int autoload_device(DCR *dcr, int writing, BSOCK *dir) { JCR *jcr = dcr->jcr; - DEVICE *dev = dcr->dev; + DEVICE * volatile dev = dcr->dev; int slot; int drive = dev->drive_index; int rtn_stat = -1; /* error status */ @@ -136,6 +136,7 @@ int autoload_device(DCR *dcr, int writing, BSOCK *dir) if (dir) { return 0; /* For user, bail out right now */ } + /* ***FIXME*** this really should not be here */ if (dir_find_next_appendable_volume(dcr)) { slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0; } else { @@ -265,7 +266,6 @@ int get_autochanger_loaded_slot(DCR *dcr) Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded? drive %d\" command.\n"), drive); changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded"); - *results.c_str() = 0; Dmsg1(100, "Run program=%s\n", changer); status = run_program_full_output(changer, timeout, results.c_str()); Dmsg3(100, "run_prog: %s stat=%d result=%s", changer, status, results.c_str()); @@ -355,7 +355,6 @@ bool unload_autochanger(DCR *dcr, int loaded) dcr->device->changer_command, "unload"); dev->close(); Dmsg1(100, "Run program=%s\n", changer); - *results.c_str() = 0; int stat = run_program_full_output(changer, timeout, results.c_str()); dcr->VolCatInfo.Slot = slot; if (stat != 0) { @@ -369,6 +368,7 @@ bool unload_autochanger(DCR *dcr, int loaded) } else { dev->Slot = 0; /* nothing loaded */ } + dev->clear_unload(); free_volume(dev); /* Free any volume associated with this drive */ free_pool_memory(changer); unlock_changer(dcr); @@ -382,14 +382,9 @@ bool unload_autochanger(DCR *dcr, int loaded) static bool unload_other_drive(DCR *dcr, int slot) { DEVICE *dev = NULL; - DEVICE *save_dev; - JCR *jcr = dcr->jcr; - int save_slot; - uint32_t timeout = dcr->device->max_changer_wait; - bool ok = true; + bool found = false; AUTOCHANGER *changer = dcr->dev->device->changer_res; DEVRES *device; - bool found = false; int retries = 0; /* wait for device retries */ if (!changer) { @@ -423,36 +418,50 @@ static bool unload_other_drive(DCR *dcr, int slot) } break; } - dev->dlock(); if (dev->is_busy()) { - Jmsg(jcr, M_WARNING, 0, _("Volume \"%s\" is in use by device %s\n"), + Jmsg(dcr->jcr, M_WARNING, 0, _("Volume \"%s\" is in use by device %s\n"), dcr->VolumeName, dev->print_name()); Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n", - dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), slot); - Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->reserved_device); - dev->dunlock(); + dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), dev->Slot); + Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->num_reserved()); + return false; + } + return unload_dev(dcr, dev); +} + +bool unload_dev(DCR *dcr, DEVICE *dev) +{ + JCR *jcr = dcr->jcr; + bool ok = true; + uint32_t timeout = dcr->device->max_changer_wait; + AUTOCHANGER *changer = dcr->dev->device->changer_res; + DEVICE *save_dev; + int save_slot; + + if (!changer || dev->Slot <= 0) { return false; } + dev->dlock(); POOLMEM *changer_cmd = get_pool_memory(PM_FNAME); POOL_MEM results(PM_MESSAGE); lock_changer(dcr); Jmsg(jcr, M_INFO, 0, _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"), - slot, dev->drive_index); + dev->Slot, dev->drive_index); Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n", - slot, dev->drive_index); + dev->Slot, dev->drive_index); save_dev = dcr->dev; dcr->dev = dev; save_slot = dcr->VolCatInfo.Slot; - dcr->VolCatInfo.Slot = slot; + dcr->VolCatInfo.Slot = dev->Slot; changer_cmd = edit_device_codes(dcr, changer_cmd, dcr->device->changer_command, "unload"); dev->close(); Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(), - dev->reserved_device); + dev->num_reserved()); Dmsg1(100, "Run program=%s\n", changer_cmd); int stat = run_program_full_output(changer_cmd, timeout, results.c_str()); dcr->VolCatInfo.Slot = save_slot; @@ -461,10 +470,10 @@ static bool unload_other_drive(DCR *dcr, int slot) berrno be; be.set_errno(stat); Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"), - slot, dev->drive_index, be.bstrerror()); + dev->Slot, dev->drive_index, be.bstrerror()); Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n", - slot, dev->drive_index, be.bstrerror()); + dev->Slot, dev->drive_index, be.bstrerror()); ok = false; dev->Slot = -1; /* unknown */ } else { @@ -472,6 +481,7 @@ static bool unload_other_drive(DCR *dcr, int slot) Dmsg0(100, "Slot unloaded\n"); } free_volume(dev); /* Free any volume associated with this drive */ + dev->clear_unload(); unlock_changer(dcr); dev->dunlock(); free_pool_memory(changer_cmd); diff --git a/bacula/src/stored/bscan.c b/bacula/src/stored/bscan.c index 2cb3655ad4..e24a53bcb5 100644 --- a/bacula/src/stored/bscan.c +++ b/bacula/src/stored/bscan.c @@ -149,7 +149,7 @@ int main (int argc, char *argv[]) OSDependentInit(); - while ((ch = getopt(argc, argv, "b:c:d:h:mn:pP:rsSu:vV:w:?")) != -1) { + while ((ch = getopt(argc, argv, "b:c:dD:h:p:mn:pP:rsSt:u:vV:w:?")) != -1) { switch (ch) { case 'S' : showProgress = true; diff --git a/bacula/src/stored/dev.c b/bacula/src/stored/dev.c index f525e0924c..079f4eac00 100644 --- a/bacula/src/stored/dev.c +++ b/bacula/src/stored/dev.c @@ -1,7 +1,7 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2000-2007 Free Software Foundation Europe e.V. + Copyright (C) 2000-2008 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. @@ -711,7 +711,7 @@ bool DEVICE::rewind(DCR *dcr) unsigned int i; bool first = true; - Dmsg3(400, "rewind res=%d fd=%d %s\n", reserved_device, m_fd, print_name()); + Dmsg3(400, "rewind res=%d fd=%d %s\n", num_reserved(), m_fd, print_name()); state &= ~(ST_EOT|ST_EOF|ST_WEOT); /* remove EOF/EOT flags */ block_num = file = 0; file_size = 0; @@ -843,7 +843,7 @@ bool DEVICE::eod(DCR *dcr) block_num = file = 0; file_size = 0; file_addr = 0; - if (is_fifo() || is_prog()) { + if (is_fifo()) { return true; } if (!is_tape()) { @@ -1917,23 +1917,73 @@ boffset_t DEVICE::lseek(DCR *dcr, boffset_t offset, int whence) bool DEVICE::truncate(DCR *dcr) /* We need the DCR for DVD-writing */ { + struct stat st; + Dmsg1(100, "truncate %s\n", print_name()); switch (dev_type) { + case B_VTL_DEV: case B_TAPE_DEV: /* maybe we should rewind and write and eof ???? */ return true; /* we don't really truncate tapes */ case B_DVD_DEV: return truncate_dvd(dcr); case B_FILE_DEV: - /* ***FIXME*** we really need to unlink() the file so that - * its name can be changed for a relabel. - */ if (ftruncate(m_fd, 0) != 0) { berrno be; Mmsg2(errmsg, _("Unable to truncate device %s. ERR=%s\n"), print_name(), be.bstrerror()); return false; } + + /* + * Check for a successful ftruncate() and issue a work-around for devices + * (mostly cheap NAS) that don't support truncation. + * Workaround supplied by Martin Schmid as a solution to bug #1011. + * 1. close file + * 2. delete file + * 3. open new file with same mode + * 4. change ownership to original + */ + + if (fstat(m_fd, &st) != 0) { + berrno be; + Mmsg2(errmsg, _("Unable to stat device %s. ERR=%s\n"), + print_name(), be.bstrerror()); + return false; + } + + if (st.st_size != 0) { /* ftruncate() didn't work */ + POOL_MEM archive_name(PM_FNAME); + + pm_strcpy(archive_name, dev_name); + if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) { + pm_strcat(archive_name, "/"); + } + pm_strcat(archive_name, dcr->VolumeName); + + Mmsg2(errmsg, _("Device %s doesn't support ftruncate(). Recreating file %s.\n"), + print_name(), archive_name.c_str()); + + /* Close file and blow it away */ + ::close(m_fd); + ::unlink(archive_name.c_str()); + + /* Recreate the file -- of course, empty */ + set_mode(CREATE_READ_WRITE); + if ((m_fd = ::open(archive_name.c_str(), mode, st.st_mode)) < 0) { + berrno be; + dev_errno = errno; + Mmsg2(errmsg, _("Could not reopen: %s, ERR=%s\n"), archive_name.c_str(), + be.bstrerror()); + Dmsg1(100, "reopen failed: %s", errmsg); + Emsg0(M_FATAL, 0, errmsg); + return false; + } + + /* Reset proper owner */ + chown(archive_name.c_str(), st.st_uid, st.st_gid); + } + return true; } return false; @@ -2002,7 +2052,6 @@ bool DEVICE::do_mount(int mount, int dotimeout) timeout = 0; } results = get_memory(4000); - results[0] = 0; /* If busy retry each second */ Dmsg1(100, "do_mount run_prog=%s\n", ocmd.c_str()); @@ -2184,12 +2233,15 @@ void DEVICE::edit_mount_codes(POOL_MEM &omsg, const char *imsg) } } -/* return the last timer interval (ms) */ +/* return the last timer interval (ms) + * or 0 if something goes wrong + */ btime_t DEVICE::get_timer_count() { - btime_t old = last_timer; + btime_t temp = last_timer; last_timer = get_current_btime(); - return last_timer - old; + temp = last_timer - temp; /* get elapsed time */ + return (temp>0)?temp:0; /* take care of skewed clock */ } /* read from fd */ diff --git a/bacula/src/stored/dev.h b/bacula/src/stored/dev.h index 45068b2826..619460ef1d 100644 --- a/bacula/src/stored/dev.h +++ b/bacula/src/stored/dev.h @@ -35,6 +35,37 @@ * */ +/* + * Some details of how volume and device reservations work + * + * class VOLRES: + * set_in_use() volume being used on current drive + * clear_in_use() no longer being used. Can be re-used or moved. + * set_swapping() set volume being moved to another drive + * is_swapping() volume is being moved to another drive + * clear_swapping() volume normal + * + * class DEVICE: + * set_load() set to load volume + * needs_load() volume must be loaded (i.e. set_load done) + * clear_load() load done. + * set_unload() set to unload volume + * needs_unload() volume must be unloaded + * clear_unload() volume unloaded + * + * reservations are temporary until the drive is acquired + * inc_reserved() increments num of reservations + * dec_reserved() decrements num of reservations + * num_reserved() number of reservations + * + * class DCR: + * set_reserved() sets local reserve flag and calls dev->inc_reserved() + * clear_reserved() clears local reserve flag and calls dev->dec_reserved() + * is_reserved() returns local reserved flag + * unreserve_device() much more complete unreservation + * + */ + #ifndef __DEV_H #define __DEV_H 1 @@ -64,7 +95,7 @@ enum { B_TAPE_DEV, B_DVD_DEV, B_FIFO_DEV, - B_PROG_DEV + B_VTL_DEV }; /* Generic status bits returned from status_dev() */ @@ -182,7 +213,11 @@ private: int m_blocked; /* set if we must wait (i.e. change tape) */ int m_count; /* Mutex use count -- DEBUG only */ pthread_t m_pid; /* Thread that locked -- DEBUG only */ + bool m_unload; /* set when Volume must be unloaded */ + bool m_load; /* set when Volume must be loaded */ + int m_num_reserved; /* counter of device reservations */ public: + DEVICE * volatile swap_dev; /* Swap vol from this device */ dlist *attached_dcrs; /* attached DCR list */ pthread_mutex_t m_mutex; /* access control */ pthread_mutex_t spool_mutex; /* mutex for updating spool_size */ @@ -192,7 +227,6 @@ public: int dev_prev_blocked; /* previous blocked state */ int num_waiting; /* number of threads waiting */ int num_writers; /* number of writing threads */ - int reserved_device; /* number of device reservations */ int capabilities; /* capabilities mask */ int state; /* state mask */ int dev_errno; /* Our own errno */ @@ -277,16 +311,17 @@ public: int is_file() const { return dev_type == B_FILE_DEV; } int is_fifo() const { return dev_type == B_FIFO_DEV; } int is_dvd() const { return dev_type == B_DVD_DEV; } - int is_prog() const { return dev_type == B_PROG_DEV; } + int is_vtl() const { return dev_type == B_VTL_DEV; } int is_open() const { return m_fd >= 0; } int is_offline() const { return state & ST_OFFLINE; } int is_labeled() const { return state & ST_LABEL; } int is_mounted() const { return state & ST_MOUNTED; } int is_unmountable() const { return (is_dvd() || (is_file() && is_removable())); } + int num_reserved() const { return m_num_reserved; }; int is_part_spooled() const { return state & ST_PART_SPOOLED; } int have_media() const { return state & ST_MEDIA; } int is_short_block() const { return state & ST_SHORT; } - int is_busy() const { return (state & ST_READ) || num_writers || reserved_device; } + int is_busy() const { return (state & ST_READ) || num_writers || num_reserved(); } int at_eof() const { return state & ST_EOF; } int at_eot() const { return state & ST_EOT; } int at_weot() const { return state & ST_WEOT; } @@ -308,6 +343,8 @@ public: (m_blocked == BST_UNMOUNTED || m_blocked == BST_WAITING_FOR_SYSOP || m_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP); }; + bool must_unload() const { return m_unload; }; + bool must_load() const { return m_load; }; const char *strerror() const; const char *archive_name() const; const char *name() const; @@ -327,8 +364,12 @@ public: void set_part_spooled(int val) { if (val) state |= ST_PART_SPOOLED; \ else state &= ~ST_PART_SPOOLED; }; + void set_unload() { m_unload = true; }; + void set_load() { m_load = true; }; + void inc_reserved() { m_num_reserved++; } void set_mounted(int val) { if (val) state |= ST_MOUNTED; \ else state &= ~ST_MOUNTED; }; + void dec_reserved() { m_num_reserved--; ASSERT(m_num_reserved>=0); }; void clear_append() { state &= ~ST_APPEND; }; void clear_read() { state &= ~ST_READ; }; void clear_labeled() { state &= ~ST_LABEL; }; @@ -340,6 +381,8 @@ public: void clear_media() { state &= ~ST_MEDIA; }; void clear_short_block() { state &= ~ST_SHORT; }; void clear_freespace_ok() { state &= ~ST_FREESPACE_OK; }; + void clear_unload() { m_unload = false; }; + void clear_load() { m_load = false; }; char *bstrerror(void) { return errmsg; }; char *print_errmsg() { return errmsg; }; @@ -419,10 +462,15 @@ inline const char *DEVICE::print_name() const { return prt_name; } * DCRs open, each pointing to a different device. */ class DCR { +private: + bool m_dev_locked; /* set if dev already locked */ + bool m_reserved; /* set if reserved device */ + bool m_found_in_use; /* set if a volume found in use */ + public: dlink dev_link; /* link to attach to dev */ JCR *jcr; /* pointer to JCR */ - DEVICE *dev; /* pointer to device */ + DEVICE * volatile dev; /* pointer to device */ DEVRES *device; /* pointer to device resource */ DEV_BLOCK *block; /* pointer to block */ DEV_RECORD *rec; /* pointer to record */ @@ -432,14 +480,12 @@ public: bool spooling; /* set when actually spooling */ bool despooling; /* set when despooling */ bool despool_wait; /* waiting for despooling */ - bool m_dev_locked; /* set if dev already locked */ bool NewVol; /* set if new Volume mounted */ bool WroteVol; /* set if Volume written */ bool NewFile; /* set when EOF written */ - bool reserved_device; /* set if reserve done */ + bool reserved_volume; /* set if we reserved a volume */ bool any_volume; /* Any OK for dir_find_next... */ bool attached_to_dev; /* set when attached to dev */ - bool volume_in_use; /* set in dir_find_next_appendable_volume() */ bool keep_dcr; /* do not free dcr in release_dcr */ uint32_t VolFirstIndex; /* First file index this Volume */ uint32_t VolLastIndex; /* Last file index this Volume */ @@ -461,22 +507,52 @@ public: VOLUME_CAT_INFO VolCatInfo; /* Catalog info for desired volume */ /* Methods */ + bool found_in_use() const { return m_found_in_use; }; + void set_found_in_use() { m_found_in_use = true; }; + void clear_found_in_use() { m_found_in_use = false; }; + bool is_reserved() const { return m_reserved; }; bool is_dev_locked() { return m_dev_locked; } void dlock() { dev->dlock(); m_dev_locked = true; } - void dunlock() { dev->dunlock(); m_dev_locked = false;} + void dunlock() { m_dev_locked = false; dev->dunlock(); } void dblock(int why) { dev->dblock(why); } + + /* Methods in reserve.c */ + void clear_reserved(); + void set_reserved(); + void unreserve_device(); + bool can_i_use_volume(); + + /* Methods in mount.c */ + bool mount_next_write_volume(); + bool mount_next_read_volume(); + void mark_volume_in_error(); + void mark_volume_not_inchanger(); + int try_autolabel(bool opened); + bool is_suitable_volume_mounted(); + bool is_eod_valid(); + int check_volume_label(bool &ask, bool &autochanger); + void release_volume(); + void do_swapping(); }; /* * Volume reservation class -- see reserve.c */ class VOLRES { + bool m_swapping; /* set when swapping to another drive */ + bool m_in_use; /* set when volume reserved or in use */ public: dlink link; char *vol_name; /* Volume name */ DEVICE *dev; /* Pointer to device to which we are attached */ - bool released; /* set when the Volume can be released */ + + bool is_swapping() const { return m_swapping; }; + void set_swapping() { m_swapping = true; }; + void clear_swapping() { m_swapping = false; }; + bool is_in_use() const { return m_in_use; }; + void set_in_use() { m_in_use = true; }; + void clear_in_use() { m_in_use = false; }; }; diff --git a/bacula/src/stored/device.c b/bacula/src/stored/device.c index 50ab5dab1a..b3c4c91fc4 100644 --- a/bacula/src/stored/device.c +++ b/bacula/src/stored/device.c @@ -1,7 +1,7 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2000-2007 Free Software Foundation Europe e.V. + Copyright (C) 2000-2008 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. @@ -122,8 +122,8 @@ bool fixup_device_block_write_error(DCR *dcr) edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2), bstrftime(dt, sizeof(dt), time(NULL))); - /* Called with have_vol=false, release=true */ - if (!mount_next_write_volume(dcr, false, true)) { + dev->set_unload(); + if (!dcr->mount_next_write_volume()) { free_block(label_blk); dcr->block = block; dev->dlock(); @@ -158,7 +158,7 @@ bool fixup_device_block_write_error(DCR *dcr) /* * Walk through all attached jcrs indicating the volume has changed */ - Dmsg1(100, "Walk attached dcrs. Volume=%s\n", dev->VolCatInfo.VolCatName); + Dmsg1(100, "Notify vol change. Volume=%s\n", dev->VolCatInfo.VolCatName); DCR *mdcr; foreach_dlist(mdcr, dev->attached_dcrs) { JCR *mjcr = mdcr->jcr; diff --git a/bacula/src/stored/dircmd.c b/bacula/src/stored/dircmd.c index a7b631a831..411d6687a3 100644 --- a/bacula/src/stored/dircmd.c +++ b/bacula/src/stored/dircmd.c @@ -915,7 +915,7 @@ static bool release_cmd(JCR *jcr) } else { /* device not being used */ Dmsg0(90, "Device not in use, releaseing\n"); unload_autochanger(dcr, -1); - release_volume(dcr); + dcr->release_volume(); dir->fsend(_("3022 Device %s released.\n"), dev->print_name()); } diff --git a/bacula/src/stored/dvd.c b/bacula/src/stored/dvd.c index 3a8241a80b..571aece352 100644 --- a/bacula/src/stored/dvd.c +++ b/bacula/src/stored/dvd.c @@ -266,7 +266,7 @@ bool dvd_write_part(DCR *dcr) Dmsg1(100, "%s\n", dev->errmsg); dev->dev_errno = EIO; if (!dev->truncating) { - mark_volume_in_error(dcr); + dcr->mark_volume_in_error(); } sm_check(__FILE__, __LINE__, false); return false; diff --git a/bacula/src/stored/label.c b/bacula/src/stored/label.c index 5ec6a72b68..3d791f4fb0 100644 --- a/bacula/src/stored/label.c +++ b/bacula/src/stored/label.c @@ -67,7 +67,7 @@ static void create_volume_label_record(DCR *dcr, DEV_RECORD *rec); int read_dev_volume_label(DCR *dcr) { JCR *jcr = dcr->jcr; - DEVICE *dev = dcr->dev; + DEVICE * volatile dev = dcr->dev; char *VolName = dcr->VolumeName; DEV_RECORD *record; bool ok = false; @@ -77,7 +77,7 @@ int read_dev_volume_label(DCR *dcr) bool have_ansi_label = false; Dmsg4(100, "Enter read_volume_label res=%d device=%s vol=%s dev_Vol=%s\n", - dev->reserved_device, dev->print_name(), VolName, + dev->num_reserved(), dev->print_name(), VolName, dev->VolHdr.VolumeName[0]?dev->VolHdr.VolumeName:"*NULL*"); if (!dev->is_open()) { @@ -212,12 +212,16 @@ int read_dev_volume_label(DCR *dcr) } dev->set_labeled(); /* set has Bacula label */ + Dmsg1(100, "Call reserve_volume=%s\n", dev->VolHdr.VolumeName); + Dmsg2(100, "=== dcr->dev=%p dev=%p\n", dcr->dev, dev); if (reserve_volume(dcr, dev->VolHdr.VolumeName) == NULL) { Mmsg2(jcr->errmsg, _("Could not reserve volume %s on %s\n"), dev->VolHdr.VolumeName, dev->print_name()); stat = VOL_NAME_ERROR; goto bail_out; } + Dmsg2(100, "=== dcr->dev=%p dev=%p\n", dcr->dev, dev); +// dev = dcr->dev; /* may have changed in reserve volume */ /* Compare Volume Names */ Dmsg2(130, "Compare Vol names: VolName=%s hdr=%s\n", VolName?VolName:"*", dev->VolHdr.VolumeName); @@ -234,6 +238,7 @@ int read_dev_volume_label(DCR *dcr) } Dmsg0(150, "return VOL_NAME_ERROR\n"); stat = VOL_NAME_ERROR; + volume_unused(dcr); /* mark volume "released" */ goto bail_out; } Dmsg1(130, "Copy vol_name=%s\n", dev->VolHdr.VolumeName); @@ -249,6 +254,7 @@ int read_dev_volume_label(DCR *dcr) stat = read_ansi_ibm_label(dcr); /* If we want a label and didn't find it, return error */ if (stat != VOL_OK) { + volume_unused(dcr); /* mark volume "released" */ goto bail_out; } } @@ -257,7 +263,6 @@ int read_dev_volume_label(DCR *dcr) return VOL_OK; bail_out: - volume_unused(dcr); /* mark volume "released" */ empty_block(block); dev->rewind(dcr); Dmsg1(150, "return %d\n", stat); @@ -310,7 +315,7 @@ bool write_volume_label_to_block(DCR *dcr) bool write_new_volume_label_to_dev(DCR *dcr, const char *VolName, const char *PoolName, bool relabel, bool dvdnow) { - DEVICE *dev = dcr->dev; + DEVICE * volatile dev = dcr->dev; Dmsg0(150, "write_volume_label()\n"); @@ -339,7 +344,6 @@ bool write_new_volume_label_to_dev(DCR *dcr, const char *VolName, } Dmsg1(150, "Label type=%d\n", dev->label_type); if (!dev->rewind(dcr)) { - dev->clear_volhdr(); Dmsg2(130, "Bad status on %s from rewind: ERR=%s\n", dev->print_name(), dev->print_errmsg()); if (!forge_on) { goto bail_out; @@ -401,11 +405,13 @@ bool write_new_volume_label_to_dev(DCR *dcr, const char *VolName, if (debug_level >= 20) { dump_volume_label(dev); } + Dmsg0(100, "Call reserve_volume\n"); if (reserve_volume(dcr, VolName) == NULL) { Mmsg2(dcr->jcr->errmsg, _("Could not reserve volume %s on %s\n"), dev->VolHdr.VolumeName, dev->print_name()); goto bail_out; } + dev = dcr->dev; /* may have changed in reserve_volume */ dev->clear_append(); /* remove append since this is PRE_LABEL */ return true; @@ -455,6 +461,7 @@ bool rewrite_volume_label(DCR *dcr, bool recycle) return false; } if (recycle) { +// volume_unused(dcr); /* mark volume unused */ if (!dev->truncate(dcr)) { Jmsg2(jcr, M_FATAL, 0, _("Truncate error on device %s: ERR=%s\n"), dev->print_name(), dev->print_errmsg()); @@ -601,7 +608,7 @@ void create_volume_label(DEVICE *dev, const char *VolName, ASSERT(dev != NULL); - dev->clear_volhdr(); /* release any old volume */ + dev->clear_volhdr(); /* clear any old volume info */ bstrncpy(dev->VolHdr.Id, BaculaId, sizeof(dev->VolHdr.Id)); dev->VolHdr.VerNum = BaculaTapeVersion; diff --git a/bacula/src/stored/lock.c b/bacula/src/stored/lock.c index 28200047b9..647dd19dcf 100644 --- a/bacula/src/stored/lock.c +++ b/bacula/src/stored/lock.c @@ -1,7 +1,7 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2000-2007 Free Software Foundation Europe e.V. + Copyright (C) 2000-2008 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. diff --git a/bacula/src/stored/mount.c b/bacula/src/stored/mount.c index 08381715bd..5b22b3fa6c 100644 --- a/bacula/src/stored/mount.c +++ b/bacula/src/stored/mount.c @@ -38,8 +38,6 @@ #include "bacula.h" /* pull in global headers */ #include "stored.h" /* pull in Storage Deamon headers */ -static void mark_volume_not_inchanger(DCR *dcr); -static int try_autolabel(DCR *dcr, bool opened); enum { try_next_vol = 1, @@ -48,6 +46,13 @@ enum { try_default }; +enum { + check_next_vol = 1, + check_ok, + check_read_vol, + check_error +}; + /* * If release is set, we rewind the current volume, * which we no longer want, and ask the user (console) @@ -60,21 +65,19 @@ enum { * impossible to get the requested Volume. * */ -bool mount_next_write_volume(DCR *dcr, bool have_vol, bool release) +bool DCR::mount_next_write_volume() { int retry = 0; bool ask = false, recycle, autochanger; - int vol_label_status; - DEVICE *dev = dcr->dev; - JCR *jcr = dcr->jcr; - DEV_BLOCK *block = dcr->block; int mode; + DCR *dcr = this; - Dmsg2(150, "Enter mount_next_volume(release=%d) dev=%s\n", release, + Dmsg2(150, "Enter mount_next_volume(release=%d) dev=%s\n", dev->must_unload(), 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 @@ -85,45 +88,59 @@ mount_next_vol: /* Ignore retry if this is poll request */ if (!dev->poll && retry++ > 4) { /* Last ditch effort before giving up, force operator to respond */ - dcr->VolCatInfo.Slot = 0; + VolCatInfo.Slot = 0; + unlock_volumes(); if (!dir_ask_sysop_to_mount_volume(dcr, ST_APPEND)) { Jmsg(jcr, M_FATAL, 0, _("Too many errors trying to mount device %s.\n"), dev->print_name()); - return false; + goto no_lock_bail_out; } + lock_volumes(); } if (job_canceled(jcr)) { Jmsg(jcr, M_FATAL, 0, _("Job %d canceled.\n"), jcr->JobId); - return false; + goto bail_out; } recycle = false; - if (release) { - Dmsg0(150, "mount_next_volume release=1\n"); - release_volume(dcr); + + if (dev->must_unload()) { ask = true; /* ask operator to mount tape */ } - - /* - * Get Director's idea of what tape we should have mounted. - * in dcr->VolCatInfo - */ - Dmsg0(200, "Before dir_find_next_appendable_volume.\n"); - if (!have_vol) { - while (!dir_find_next_appendable_volume(dcr)) { - Dmsg0(200, "not dir_find_next\n"); - if (!dir_ask_sysop_to_create_appendable_volume(dcr)) { - return false; - } - Dmsg0(200, "Again dir_find_next_append...\n"); - } - } else { - have_vol = false; /* set false for next pass if any */ + do_swapping(); + + if (!is_suitable_volume_mounted()) { + bool have_vol = false; + /* Do we have a candidate volume? */ + if (dev->vol) { + bstrncpy(VolumeName, dev->vol->vol_name, sizeof(VolumeName)); + have_vol = dir_get_volume_info(this, GET_VOL_INFO_FOR_WRITE); + } + /* + * Get Director's idea of what tape we should have mounted. + * in dcr->VolCatInfo + */ + if (!have_vol) { + Dmsg0(200, "Before dir_find_next_appendable_volume.\n"); + while (!dir_find_next_appendable_volume(dcr)) { + Dmsg0(200, "not dir_find_next\n"); + if (job_canceled(jcr)) { + goto bail_out; + } + unlock_volumes(); + if (!dir_ask_sysop_to_create_appendable_volume(dcr)) { + goto no_lock_bail_out; + } + lock_volumes(); + Dmsg0(200, "Again dir_find_next_append...\n"); + } + goto mount_next_vol; + } } if (job_canceled(jcr)) { - return false; + goto bail_out; } Dmsg3(150, "After find_next_append. Vol=%s Slot=%d Parts=%d\n", - dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.Slot, dcr->VolCatInfo.VolCatParts); + VolCatInfo.VolCatName, VolCatInfo.Slot, VolCatInfo.VolCatParts); /* * Get next volume and ready it for append @@ -141,7 +158,7 @@ mount_next_vol: ask = false; } else { autochanger = false; - dcr->VolCatInfo.Slot = 0; + VolCatInfo.Slot = 0; } Dmsg1(200, "autoload_dev returns %d\n", autochanger); /* @@ -150,7 +167,7 @@ mount_next_vol: * and read the label. If there is no tape in the drive, * we will fail, recurse and ask the operator the next time. */ - if (!release && dev->is_tape() && dev->has_cap(CAP_AUTOMOUNT)) { + if (!dev->must_unload() && dev->is_tape() && dev->has_cap(CAP_AUTOMOUNT)) { Dmsg0(150, "(1)Ask=0\n"); ask = false; /* don't ask SYSOP this time */ } @@ -160,16 +177,17 @@ mount_next_vol: ask = false; } Dmsg2(150, "Ask=%d autochanger=%d\n", ask, autochanger); - release = true; /* release next time if we "recurse" */ + dev->must_unload(); /* release next time if we "recurse" */ if (ask && !dir_ask_sysop_to_mount_volume(dcr, ST_APPEND)) { Dmsg0(150, "Error return ask_sysop ...\n"); - return false; /* error return */ + goto bail_out; /* error return */ } if (job_canceled(jcr)) { - return false; + goto bail_out; } - Dmsg2(150, "want vol=%s dev=%s\n", dcr->VolumeName, dev->VolHdr.VolumeName); + Dmsg3(150, "want vol=%s devvol=%s dev=%s\n", VolumeName, + dev->VolHdr.VolumeName, dev->print_name()); if (dev->poll && dev->has_cap(CAP_CLOSEONPOLL)) { dev->close(); @@ -183,7 +201,7 @@ mount_next_vol: } /* Try autolabel if enabled */ if (dev->open(dcr, mode) < 0) { - try_autolabel(dcr, false); /* try to create a new volume label */ + try_autolabel(false); /* try to create a new volume label */ } while (dev->open(dcr, mode) < 0) { Dmsg1(150, "open_device failed: ERR=%s\n", dev->bstrerror()); @@ -204,7 +222,7 @@ mount_next_vol: dev->unmount(0); } } - if (try_autolabel(dcr, false) == try_read_vol) { + if (try_autolabel(false) == try_read_vol) { break; /* created a new volume label */ } /* If DVD, ignore the error, very often you cannot open the device @@ -214,59 +232,135 @@ mount_next_vol: } else { Jmsg(jcr, M_ERROR, 0, _("Could not open device %s: ERR=%s\n"), dev->print_name(), dev->print_errmsg()); - return false; + goto bail_out; } } /* - * Now make sure we have the right tape mounted + * Now check the volume label to make sure we have the right tape mounted */ read_volume: + + switch (check_volume_label(ask, autochanger)) { + case check_next_vol: + goto mount_next_vol; + case check_read_vol: + goto read_volume; + case check_error: + goto bail_out; + case check_ok: + break; + } + + /* + * See if we have a fresh tape or a tape with data. + * + * Note, if the LabelType is PRE_LABEL, it was labeled + * but never written. If so, rewrite the label but set as + * VOL_LABEL. We rewind and return the label (reconstructed) + * in the block so that in the case of a new tape, data can + * be appended just after the block label. If we are writing + * a second volume, the calling routine will write the label + * before writing the overflow block. + * + * If the tape is marked as Recycle, we rewrite the label. + */ + recycle = strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0; + if (dev->VolHdr.LabelType == PRE_LABEL || recycle) { + if (!rewrite_volume_label(dcr, recycle)) { + mark_volume_in_error(); + goto mount_next_vol; + } + } else { + /* + * OK, at this point, we have a valid Bacula label, but + * 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"); + Jmsg(jcr, M_INFO, 0, _("Volume \"%s\" previously written, moving to end of data.\n"), + VolumeName); + + if (!dev->eod(dcr)) { + 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()) { + goto mount_next_vol; + } + + dev->VolCatInfo.VolCatMounts++; /* Update mounts */ + Dmsg1(150, "update volinfo mounts=%d\n", dev->VolCatInfo.VolCatMounts); + if (!dir_update_volume_info(dcr, false, false)) { + goto bail_out; + } + + /* Return an empty block */ + empty_block(block); /* we used it for reading so set for write */ + } + dev->set_append(); + Dmsg1(150, "set APPEND, normal return from mount_next_write_volume. dev=%s\n", + dev->print_name()); + + unlock_volumes(); + return true; + +bail_out: + unlock_volumes(); + +no_lock_bail_out: + return false; +} + +int DCR::check_volume_label(bool &ask, bool &autochanger) +{ + int vol_label_status; /* * If we are writing to a stream device, ASSUME the volume label * is correct. */ if (dev->has_cap(CAP_STREAM)) { vol_label_status = VOL_OK; - create_volume_label(dev, dcr->VolumeName, "Default", false /* not DVD */); + create_volume_label(dev, VolumeName, "Default", false /* not DVD */); dev->VolHdr.LabelType = PRE_LABEL; } else { - vol_label_status = read_dev_volume_label(dcr); + vol_label_status = read_dev_volume_label(this); } if (job_canceled(jcr)) { - return false; + goto check_bail_out; } - Dmsg2(150, "Want dirVol=%s dirStat=%s\n", dcr->VolumeName, - dcr->VolCatInfo.VolCatStatus); + 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. */ switch (vol_label_status) { case VOL_OK: - Dmsg1(150, "Vol OK name=%s\n", dcr->VolumeName); - dev->VolCatInfo = dcr->VolCatInfo; /* structure assignment */ - recycle = strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0; + Dmsg1(150, "Vol OK name=%s\n", dev->VolHdr.VolumeName); + dev->VolCatInfo = VolCatInfo; /* structure assignment */ break; /* got a Volume */ case VOL_NAME_ERROR: VOLUME_CAT_INFO dcrVolCatInfo, devVolCatInfo; - char VolumeName[MAX_NAME_LENGTH]; + char saveVolumeName[MAX_NAME_LENGTH]; /* If not removable, Volume is broken */ if (!dev->is_removable()) { Jmsg(jcr, M_WARNING, 0, _("Volume \"%s\" not on device %s.\n"), - dcr->VolumeName, dev->print_name()); - mark_volume_in_error(dcr); - goto mount_next_vol; + VolumeName, dev->print_name()); + mark_volume_in_error(); + goto check_next_volume; } - Dmsg1(150, "Vol NAME Error Name=%s\n", dcr->VolumeName); + Dmsg1(150, "Vol NAME Error Name=%s\n", VolumeName); /* If polling and got a previous bad name, ignore it */ if (dev->poll && strcmp(dev->BadVolName, dev->VolHdr.VolumeName) == 0) { ask = true; - Dmsg1(200, "Vol Name error supress due to poll. Name=%s\n", dcr->VolumeName); - goto mount_next_vol; + Dmsg1(200, "Vol Name error supress due to poll. Name=%s\n", VolumeName); + goto check_next_volume; } /* * OK, we got a different volume mounted. First save the @@ -274,24 +368,24 @@ read_volume: * this volume is really OK. If not, put back the desired * volume name, mark it not in changer and continue. */ - dcrVolCatInfo = dcr->VolCatInfo; /* structure assignment */ + dcrVolCatInfo = VolCatInfo; /* structure assignment */ devVolCatInfo = dev->VolCatInfo; /* structure assignment */ /* Check if this is a valid Volume in the pool */ - bstrncpy(VolumeName, dcr->VolumeName, sizeof(VolumeName)); - bstrncpy(dcr->VolumeName, dev->VolHdr.VolumeName, sizeof(dcr->VolumeName)); - if (!dir_get_volume_info(dcr, GET_VOL_INFO_FOR_WRITE)) { + bstrncpy(saveVolumeName, VolumeName, sizeof(saveVolumeName)); + bstrncpy(VolumeName, dev->VolHdr.VolumeName, sizeof(VolumeName)); + if (!dir_get_volume_info(this, GET_VOL_INFO_FOR_WRITE)) { POOL_MEM vol_info_msg; pm_strcpy(vol_info_msg, jcr->dir_bsock->msg); /* save error message */ /* Restore desired volume name, note device info out of sync */ /* This gets the info regardless of the Pool */ - bstrncpy(dcr->VolumeName, dev->VolHdr.VolumeName, sizeof(dcr->VolumeName)); - if (autochanger && !dir_get_volume_info(dcr, GET_VOL_INFO_FOR_READ)) { + bstrncpy(VolumeName, dev->VolHdr.VolumeName, sizeof(VolumeName)); + if (autochanger && !dir_get_volume_info(this, GET_VOL_INFO_FOR_READ)) { /* * If we get here, we know we cannot write on the Volume, * and we know that we cannot read it either, so it * is not in the autochanger. */ - mark_volume_not_inchanger(dcr); + mark_volume_not_inchanger(); } dev->VolCatInfo = devVolCatInfo; /* structure assignment */ bstrncpy(dev->BadVolName, dev->VolHdr.VolumeName, sizeof(dev->BadVolName)); @@ -302,17 +396,16 @@ read_volume: vol_info_msg.c_str()); ask = true; /* Restore saved DCR before continuing */ - bstrncpy(dcr->VolumeName, VolumeName, sizeof(dcr->VolumeName)); - dcr->VolCatInfo = dcrVolCatInfo; /* structure assignment */ - goto mount_next_vol; + bstrncpy(VolumeName, saveVolumeName, sizeof(VolumeName)); + VolCatInfo = dcrVolCatInfo; /* structure assignment */ + goto check_next_volume; } /* * This was not the volume we expected, but it is OK with * the Director, so use it. */ - Dmsg1(150, "want new name=%s\n", dcr->VolumeName); - dev->VolCatInfo = dcr->VolCatInfo; /* structure assignment */ - recycle = strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0; + Dmsg1(150, "Got new Volume name=%s\n", VolumeName); + dev->VolCatInfo = VolCatInfo; /* structure assignment */ break; /* got a Volume */ /* * At this point, we assume we have a blank tape mounted. @@ -320,18 +413,18 @@ read_volume: case VOL_IO_ERROR: if (dev->is_dvd()) { Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg); - mark_volume_in_error(dcr); - return false; /* we could not write on DVD */ + mark_volume_in_error(); + goto check_bail_out; /* we could not write on DVD */ } /* Fall through wanted */ case VOL_NO_LABEL: - switch (try_autolabel(dcr, true)) { + switch (try_autolabel(true)) { case try_next_vol: - goto mount_next_vol; + goto check_next_volume; case try_read_vol: - goto read_volume; + goto check_read_volume; case try_error: - return false; + goto check_bail_out; case try_default: break; } @@ -350,135 +443,123 @@ read_volume: if (dev->requires_mount()) { dev->close(); } - goto mount_next_vol; + goto check_next_volume; } + return check_ok; + +check_next_volume: + return check_next_vol; + +check_bail_out: + return check_error; + +check_read_volume: + return check_read_vol; + +} + + +bool DCR::is_suitable_volume_mounted() +{ + /* Volume mounted? */ + if (dev->VolHdr.VolumeName[0] == 0 || dev->swap_dev || dev->must_unload()) { + return false; /* no */ + } + bstrncpy(VolumeName, dev->VolHdr.VolumeName, sizeof(VolumeName)); + return dir_get_volume_info(this, GET_VOL_INFO_FOR_WRITE); +} + +void DCR::do_swapping() +{ + if (dev->must_unload()) { + Dmsg0(150, "mount_next_volume release=1\n"); + unload_autochanger(this, -1); + release_volume(); + dev->clear_unload(); + } /* - * See if we have a fresh tape or a tape with data. - * - * Note, if the LabelType is PRE_LABEL, it was labeled - * but never written. If so, rewrite the label but set as - * VOL_LABEL. We rewind and return the label (reconstructed) - * in the block so that in the case of a new tape, data can - * be appended just after the block label. If we are writing - * a second volume, the calling routine will write the label - * before writing the overflow block. - * - * If the tape is marked as Recycle, we rewrite the label. + * See if we are asked to swap the Volume from another device + * if so, unload the other device here, and attach the + * volume to our drive. */ - if (dev->VolHdr.LabelType == PRE_LABEL || recycle) { - if (!rewrite_volume_label(dcr, recycle)) { - mark_volume_in_error(dcr); - goto mount_next_vol; + if (dev->swap_dev) { + Dmsg1(100, "Swap unloading %s\n", dev->swap_dev->print_name()); + if (dev->swap_dev->must_unload()) { + unload_dev(this, dev->swap_dev); } - } else { - /* - * OK, at this point, we have a valid Bacula label, but - * 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"); - Jmsg(jcr, M_INFO, 0, _("Volume \"%s\" previously written, moving to end of data.\n"), - dcr->VolumeName); - if (!dev->eod(dcr)) { - 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(dcr); - goto mount_next_vol; - } - if (dev->is_dvd()) { - char ed1[50], ed2[50]; - if (dev->VolCatInfo.VolCatBytes == dev->part_start + dev->part_size) { - Jmsg(jcr, M_INFO, 0, _("Ready to append to end of Volume \"%s\"" - " part=%d size=%s\n"), dcr->VolumeName, - dev->part, edit_uint64(dev->VolCatInfo.VolCatBytes,ed1)); - } else { - Jmsg(jcr, M_ERROR, 0, _("Bacula cannot write on DVD Volume \"%s\" because: " - "The sizes do not match! Volume=%s Catalog=%s\n"), - dcr->VolumeName, - edit_uint64(dev->part_start + dev->part_size, ed1), - edit_uint64(dev->VolCatInfo.VolCatBytes, ed2)); - mark_volume_in_error(dcr); - goto mount_next_vol; - } - } else if (dev->is_tape()) { - /* - * Check if we are positioned on the tape at the same place - * that the database says we should be. - */ - if (dev->VolCatInfo.VolCatFiles == dev->get_file()) { - Jmsg(jcr, M_INFO, 0, _("Ready to append to end of Volume \"%s\" at file=%d.\n"), - dcr->VolumeName, dev->get_file()); - } 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"), - dcr->VolumeName, dev->get_file(), dev->VolCatInfo.VolCatFiles); - mark_volume_in_error(dcr); - goto mount_next_vol; - } - } else if (dev->is_file()) { - char ed1[50], ed2[50]; - boffset_t pos; - pos = dev->lseek(dcr, (boffset_t)0, SEEK_END); - if (dev->VolCatInfo.VolCatBytes == (uint64_t)pos) { - Jmsg(jcr, M_INFO, 0, _("Ready to append to end of Volume \"%s\"" - " size=%s\n"), dcr->VolumeName, - edit_uint64(dev->VolCatInfo.VolCatBytes, ed1)); - } else { - Jmsg(jcr, M_ERROR, 0, _("Bacula cannot write on disk Volume \"%s\" because: " - "The sizes do not match! Volume=%s Catalog=%s\n"), - dcr->VolumeName, - edit_uint64(pos, ed1), - edit_uint64(dev->VolCatInfo.VolCatBytes, ed2)); - mark_volume_in_error(dcr); - goto mount_next_vol; - } + if (dev->vol) { + dev->vol->clear_swapping(); + dev->vol->set_in_use(); + dev->VolHdr.VolumeName[0] = 0; /* don't yet have right Volume */ } - dev->VolCatInfo.VolCatMounts++; /* Update mounts */ - Dmsg1(150, "update volinfo mounts=%d\n", dev->VolCatInfo.VolCatMounts); - if (!dir_update_volume_info(dcr, false, false)) { + dev->swap_dev = NULL; + } + if (dev->must_load()) { + dev->clear_load(); + dev->clear_volhdr(); /* force "load" */ + } +} + + +/* + * Check if the current position on the volume corresponds to + * what is in the catalog. + */ +bool DCR::is_eod_valid() +{ + if (dev->is_dvd()) { + char ed1[50], ed2[50]; + if (dev->VolCatInfo.VolCatBytes == dev->part_start + dev->part_size) { + Jmsg(jcr, M_INFO, 0, _("Ready to append to end of Volume \"%s\"" + " part=%d size=%s\n"), VolumeName, + dev->part, edit_uint64(dev->VolCatInfo.VolCatBytes,ed1)); + } else { + Jmsg(jcr, M_ERROR, 0, _("Bacula cannot write on DVD Volume \"%s\" because: " + "The sizes do not match! Volume=%s Catalog=%s\n"), + VolumeName, + edit_uint64(dev->part_start + dev->part_size, ed1), + edit_uint64(dev->VolCatInfo.VolCatBytes, ed2)); + mark_volume_in_error(); return false; } - + } else if (dev->is_tape()) { /* - * DVD : check if the last part was removed or truncated, or if a written - * part was overwritten. - * We need to do it after dir_update_volume_info, so we have the EndBlock - * info. (nb: I don't understand why VolCatFiles is set (used to check - * tape file number), but not EndBlock) - * Maybe could it be changed "dev->is_file()" (would remove the fixme above) - * - * Disabled: I had problems with this code... - * (maybe is it related to the seek bug ?) + * Check if we are positioned on the tape at the same place + * that the database says we should be. */ -#ifdef xxx - if (dev->is_dvd()) { - Dmsg2(150, "DVD/File sanity check addr=%u vs endblock=%u\n", (unsigned int)dev->file_addr, (unsigned int)dev->VolCatInfo.EndBlock); - if (dev->file_addr == dev->VolCatInfo.EndBlock+1) { - Jmsg(jcr, M_INFO, 0, _("Ready to append to end of Volume \"%s\" at file address=%u.\n"), - dcr->VolumeName, (unsigned int)dev->file_addr); - } - else { - Jmsg(jcr, M_ERROR, 0, _("Bacula cannot write on Volume \"%s\" because:\n" - "The EOD file address is wrong: Volume file address=%u != Catalog Endblock=%u(+1)\n" - "Perhaps You removed the DVD last part in spool directory.\n"), - dcr->VolumeName, (unsigned int)dev->file_addr, (unsigned int)dev->VolCatInfo.EndBlock); - mark_volume_in_error(dcr); - goto mount_next_vol; - } + 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 { + Jmsg(jcr, M_ERROR, 0, _("Bacula cannot write on tape Volume \"%s\" because:\n" + "The number of files mismatch! Volume=%u Catalog=%u\n"), + VolumeName, dev->get_file(), dev->VolCatInfo.VolCatFiles); + mark_volume_in_error(); + return false; + } + } else if (dev->is_file()) { + char ed1[50], ed2[50]; + boffset_t pos; + pos = dev->lseek(this, (boffset_t)0, SEEK_END); + if (dev->VolCatInfo.VolCatBytes == (uint64_t)pos) { + Jmsg(jcr, M_INFO, 0, _("Ready to append to end of Volume \"%s\"" + " size=%s\n"), VolumeName, + edit_uint64(dev->VolCatInfo.VolCatBytes, ed1)); + } else { + Jmsg(jcr, M_ERROR, 0, _("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)); + mark_volume_in_error(); + return false; } -#endif - - /* Return an empty block */ - empty_block(block); /* we used it for reading so set for write */ } - dev->set_append(); - Dmsg1(150, "set APPEND, normal return from mount_next_write_volume. dev=%s\n", - dev->print_name()); - return true; } + /* * If permitted, we label the device, make sure we can do * it by checking that the VolCatBytes is zero => not labeled, @@ -496,9 +577,9 @@ read_volume: * try_error hard error (catalog update) * try_default I couldn't do anything */ -static int try_autolabel(DCR *dcr, bool opened) +int DCR::try_autolabel(bool opened) { - DEVICE *dev = dcr->dev; + DCR *dcr = this; if (dev->poll && !dev->is_tape()) { return try_default; /* if polling, don't try to create new labels */ @@ -507,38 +588,38 @@ static int try_autolabel(DCR *dcr, bool opened) if (!opened && dev->is_tape()) { return try_default; } - if (dev->has_cap(CAP_LABEL) && (dcr->VolCatInfo.VolCatBytes == 0 || - (!dev->is_tape() && strcmp(dcr->VolCatInfo.VolCatStatus, + if (dev->has_cap(CAP_LABEL) && (VolCatInfo.VolCatBytes == 0 || + (!dev->is_tape() && strcmp(VolCatInfo.VolCatStatus, "Recycle") == 0))) { Dmsg0(150, "Create volume label\n"); /* Create a new Volume label and write it to the device */ - if (!write_new_volume_label_to_dev(dcr, dcr->VolumeName, - dcr->pool_name, false, /* no relabel */ false /* defer DVD label */)) { + if (!write_new_volume_label_to_dev(dcr, VolumeName, + pool_name, false, /* no relabel */ false /* defer DVD label */)) { Dmsg0(150, "!write_vol_label\n"); if (opened) { - mark_volume_in_error(dcr); + mark_volume_in_error(); } return try_next_vol; } Dmsg0(150, "dir_update_vol_info. Set Append\n"); /* Copy Director's info into the device info */ - dev->VolCatInfo = dcr->VolCatInfo; /* structure assignment */ + dev->VolCatInfo = VolCatInfo; /* structure assignment */ if (!dir_update_volume_info(dcr, true, true)) { /* indicate tape labeled */ return try_error; } Jmsg(dcr->jcr, M_INFO, 0, _("Labeled new Volume \"%s\" on device %s.\n"), - dcr->VolumeName, dev->print_name()); + VolumeName, dev->print_name()); return try_read_vol; /* read label we just wrote */ } - if (!dev->has_cap(CAP_LABEL) && dcr->VolCatInfo.VolCatBytes == 0) { - Jmsg(dcr->jcr, M_WARNING, 0, _("Device %s not configured to autolabel Volumes.\n"), + if (!dev->has_cap(CAP_LABEL) && VolCatInfo.VolCatBytes == 0) { + Jmsg(jcr, M_WARNING, 0, _("Device %s not configured to autolabel Volumes.\n"), dev->print_name()); } /* If not removable, Volume is broken */ if (!dev->is_removable()) { - Jmsg(dcr->jcr, M_WARNING, 0, _("Volume \"%s\" not on device %s.\n"), - dcr->VolumeName, dev->print_name()); - mark_volume_in_error(dcr); + Jmsg(jcr, M_WARNING, 0, _("Volume \"%s\" not on device %s.\n"), + VolumeName, dev->print_name()); + mark_volume_in_error(); return try_next_vol; } return try_default; @@ -548,45 +629,40 @@ static int try_autolabel(DCR *dcr, bool opened) /* * Mark volume in error in catalog */ -void mark_volume_in_error(DCR *dcr) +void DCR::mark_volume_in_error() { - DEVICE *dev = dcr->dev; - Jmsg(dcr->jcr, M_INFO, 0, _("Marking Volume \"%s\" in Error in Catalog.\n"), - dcr->VolumeName); - dev->VolCatInfo = dcr->VolCatInfo; /* structure assignment */ + Jmsg(jcr, M_INFO, 0, _("Marking Volume \"%s\" in Error in Catalog.\n"), + VolumeName); + dev->VolCatInfo = VolCatInfo; /* structure assignment */ bstrncpy(dev->VolCatInfo.VolCatStatus, "Error", sizeof(dev->VolCatInfo.VolCatStatus)); Dmsg0(150, "dir_update_vol_info. Set Error.\n"); - dir_update_volume_info(dcr, false, false); - volume_unused(dcr); + dir_update_volume_info(this, false, false); + volume_unused(this); } /* * The Volume is not in the correct slot, so mark this * Volume as not being in the Changer. */ -static void mark_volume_not_inchanger(DCR *dcr) +void DCR::mark_volume_not_inchanger() { - JCR *jcr = dcr->jcr; - DEVICE *dev = dcr->dev; Jmsg(jcr, M_ERROR, 0, _("Autochanger Volume \"%s\" not found in slot %d.\n" " Setting InChanger to zero in catalog.\n"), - dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.Slot); - dev->VolCatInfo = dcr->VolCatInfo; /* structure assignment */ - dcr->VolCatInfo.InChanger = false; + VolCatInfo.VolCatName, VolCatInfo.Slot); + dev->VolCatInfo = VolCatInfo; /* structure assignment */ + VolCatInfo.InChanger = false; dev->VolCatInfo.InChanger = false; Dmsg0(400, "update vol info in mount\n"); - dir_update_volume_info(dcr, true, false); /* set new status */ + dir_update_volume_info(this, true, false); /* set new status */ } /* * Either because we are going to hang a new volume, or because * of explicit user request, we release the current volume. */ -void release_volume(DCR *dcr) +void DCR::release_volume() { - JCR *jcr = dcr->jcr; - DEVICE *dev = dcr->dev; - if (dcr->WroteVol) { + if (WroteVol) { Jmsg0(jcr, M_ERROR, 0, _("Hey!!!!! WroteVol non-zero !!!!!\n")); Dmsg0(190, "Hey!!!!! WroteVol non-zero !!!!!\n"); } @@ -597,14 +673,14 @@ void release_volume(DCR *dcr) dev->block_num = dev->file = 0; dev->EndBlock = dev->EndFile = 0; memset(&dev->VolCatInfo, 0, sizeof(dev->VolCatInfo)); -// memset(&dcr->VolCatInfo, 0, sizeof(dcr->VolCatInfo)); + memset(&VolCatInfo, 0, sizeof(VolCatInfo)); dev->clear_volhdr(); /* Force re-read of label */ dev->clear_labeled(); dev->clear_read(); dev->clear_append(); dev->label_type = B_BACULA_LABEL; -// dcr->VolumeName[0] = 0; + VolumeName[0] = 0; if (dev->is_open() && (!dev->is_tape() || !dev->has_cap(CAP_ALWAYSOPEN))) { dev->close(); diff --git a/bacula/src/stored/protos.h b/bacula/src/stored/protos.h index b6aca7b9ff..ef8ebc09f8 100644 --- a/bacula/src/stored/protos.h +++ b/bacula/src/stored/protos.h @@ -1,7 +1,7 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2000-2007 Free Software Foundation Europe e.V. + Copyright (C) 2000-2008 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. @@ -185,9 +185,7 @@ BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev); bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec); /* From mount.c */ -bool mount_next_write_volume(DCR *dcr, bool have_vol, bool release); bool mount_next_read_volume(DCR *dcr); -void mark_volume_in_error(DCR *dcr); /* From parse_bsr.c */ BSR *parse_bsr(JCR *jcr, char *lf); @@ -220,7 +218,6 @@ void _lock_reservations(); void _unlock_reservations(); void _lock_volumes(); void _unlock_volumes(); -void release_volume(DCR *dcr); VOLRES *reserve_volume(DCR *dcr, const char *VolumeName); VOLRES *find_volume(const char *VolumeName); bool free_volume(DEVICE *dev); diff --git a/bacula/src/stored/reserve.c b/bacula/src/stored/reserve.c index e654a912a6..429cc87b71 100644 --- a/bacula/src/stored/reserve.c +++ b/bacula/src/stored/reserve.c @@ -55,6 +55,7 @@ static bool reserve_device_for_append(DCR *dcr, RCTX &rctx); static bool use_storage_cmd(JCR *jcr); static void queue_reserve_message(JCR *jcr); static void pop_reserve_messages(JCR *jcr); +//void switch_device(DCR *dcr, DEVICE *dev); /* Requests from the Director daemon */ static char use_storage[] = "use storage=%127s media_type=%127s " @@ -84,7 +85,10 @@ static int my_compare(void *item1, void *item2) return strcmp(((VOLRES *)item1)->vol_name, ((VOLRES *)item2)->vol_name); } - +/* + * This allows a given thread to recursively call lock_reservations. + * It must, of course, call unlock_... the same number of times. + */ void init_reservations_lock() { int errstat; @@ -134,6 +138,9 @@ void _unlock_reservations() int vol_list_lock_count = 0; +/* + * This allows a given thread to recursively call to lock_volumes() + */ void _lock_volumes() { int errstat; @@ -173,10 +180,11 @@ static void debug_list_volumes(const char *imsg) lock_volumes(); foreach_dlist(vol, vol_list) { if (vol->dev) { - Mmsg(msg, "List from %s: %s at %p on device %s\n", imsg, - vol->vol_name, vol->vol_name, vol->dev->print_name()); + Mmsg(msg, "List %s: %s in_use=%d on device %s\n", imsg, + vol->vol_name, vol->is_in_use(), vol->dev->print_name()); } else { - Mmsg(msg, "List from %s: %s at %p no dev\n", imsg, vol->vol_name, vol->vol_name); + Mmsg(msg, "List %s: %s in_use=%d no dev\n", imsg, vol->vol_name, + vol->is_in_use()); } Dmsg2(dbglvl, "jid=%u %s", jid(), msg.c_str()); } @@ -185,14 +193,13 @@ static void debug_list_volumes(const char *imsg) DEVICE *dev = NULL; foreach_dlist(vol, vol_list) { if (vol->dev == dev) { - Dmsg0(000, "Two Volumes on same device.\n"); + Dmsg0(dbglvl, "Two Volumes on same device.\n"); ASSERT(0); dev = vol->dev; } } #endif -// Dmsg2(dbglvl, "List from %s: %d volumes\n", imsg, count); unlock_volumes(); } @@ -212,11 +219,13 @@ void list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg) if (dev) { len = Mmsg(msg, "%s on device %s\n", vol->vol_name, dev->print_name()); sendit(msg.c_str(), len, arg); - len = Mmsg(msg, " Reader=%d writers=%d reserved=%d released=%d\n", - dev->can_read()?1:0, dev->num_writers, dev->reserved_device, vol->released); + len = Mmsg(msg, " Reader=%d writers=%d devres=%d volinuse=%d\n", + dev->can_read()?1:0, dev->num_writers, dev->num_reserved(), + vol->is_in_use()); sendit(msg.c_str(), len, arg); } else { - len = Mmsg(msg, "%s no device. released=%d\n", vol->vol_name, vol->released); + len = Mmsg(msg, "%s no device. volinuse= %d\n", vol->vol_name, + vol->is_in_use()); sendit(msg.c_str(), len, arg); } } @@ -269,7 +278,7 @@ static void free_vol_item(VOLRES *vol) * * 1. The Volume list entry must be attached to the drive (rather than * attached to a job as it currently is. I.e. the drive that "owns" - * the volume (reserved, in use, mounted) + * the volume (in use, mounted) * must point to the volume (still to be maintained in a list). * * 2. The Volume is entered in the list when a drive is reserved. @@ -310,11 +319,12 @@ static void free_vol_item(VOLRES *vol) VOLRES *reserve_volume(DCR *dcr, const char *VolumeName) { VOLRES *vol, *nvol; - DEVICE *dev = dcr->dev; + DEVICE * volatile dev = dcr->dev; ASSERT(dev != NULL); - Dmsg2(dbglvl, "jid=%u reserve_volume %s\n", jid(), VolumeName); + Dmsg3(dbglvl, "jid=%u enter reserve_volume=%s drive=%s\n", jid(), VolumeName, + dcr->dev->print_name()); /* * We lock the reservations system here to ensure * when adding a new volume that no newly scheduled @@ -328,27 +338,29 @@ VOLRES *reserve_volume(DCR *dcr, const char *VolumeName) */ if (dev->vol) { vol = dev->vol; - Dmsg5(dbglvl, "jid=%u Vol attached=%s, newvol=%s release=%d on %s\n", - jid(), vol->vol_name, VolumeName, vol->released, dev->print_name()); + Dmsg5(dbglvl, "jid=%u Vol attached=%s, newvol=%s volinuse=%d on %s\n", + jid(), vol->vol_name, VolumeName, vol->is_in_use(), dev->print_name()); /* * Make sure we don't remove the current volume we are inserting * because it was probably inserted by another job, or it - * is not being used and is marked as released. + * is not being used and is marked as not reserved. */ if (strcmp(vol->vol_name, VolumeName) == 0) { - Dmsg2(dbglvl, "jid=%u === OK, vol=%s on device. set not released.\n", jid(), VolumeName); - vol->released = false; /* retake vol if released previously */ + Dmsg3(dbglvl, "jid=%u === set reserved vol=%s dev=%s\n", jid(), VolumeName, + vol->dev->print_name()); + vol->set_in_use(); /* retake vol if released previously */ + dcr->reserved_volume = true; /* reserved volume */ goto get_out; /* Volume already on this device */ } else { - /* Don't release a volume if it is in use */ - if (!vol->released) { - Dmsg2(dbglvl, "jid=%u Cannot free vol=%s. It is not released.\n", jid(), vol->vol_name); + /* Don't release a volume if it was reserved by someone other than us */ + if (vol->is_in_use() && !dcr->reserved_volume) { + Dmsg2(dbglvl, "jid=%u Cannot free vol=%s. It is reserved.\n", jid(), vol->vol_name); vol = NULL; /* vol in use */ goto get_out; } Dmsg3(dbglvl, "jid=%u reserve_vol free vol=%s at %p\n", jid(), vol->vol_name, vol->vol_name); -// unload_autochanger(dcr, -1); /* unload the volume */ free_volume(dev); + dev->set_unload(); /* have to unload current volume */ debug_list_volumes("reserve_vol free"); } } @@ -373,35 +385,44 @@ VOLRES *reserve_volume(DCR *dcr, const char *VolumeName) * Clear dev pointer so that free_vol_item() doesn't * take away our volume. */ - nvol->dev = NULL; /* don't zap dev entry */ + nvol->dev = NULL; /* don't zap dev entry */ free_vol_item(nvol); /* Check if we are trying to use the Volume on a different drive */ if (dev != vol->dev) { /* Caller wants to switch Volume to another device */ - if (!vol->dev->is_busy()) { - /* OK to move it -- I'm not sure this will work */ - Dmsg4(dbglvl, "==== jid=%u Swap vol=%s from dev=%s to %s\n", jid(), VolumeName, - vol->dev->print_name(), dev->print_name()); - vol->dev->vol = NULL; /* take vol from old drive */ - vol->dev->VolHdr.VolumeName[0] = 0; - vol->dev = dev; /* point vol at new drive */ - dev->vol = vol; /* point dev at vol */ - dev->VolHdr.VolumeName[0] = 0; + if (!vol->dev->is_busy() && !vol->is_swapping()) { + Dmsg4(dbglvl, "==== jid=%u Swap vol=%s from dev=%s to %s\n", jid(), + VolumeName, vol->dev->print_name(), dev->print_name()); + free_volume(dev); /* free any volume attached to our drive */ + vol->dev->set_unload(); /* unload our drive */ + vol->set_swapping(); /* swap from other drive */ + dev->swap_dev = vol->dev; /* remember to get this vol */ + vol->dev->set_load(); /* then reload on our drive */ + vol->dev->vol = NULL; /* remove volume from other drive */ + vol->dev = dev; /* point it at our drive */ + dev->vol = vol; /* point our drive at it */ + Dmsg3(dbglvl, "==== Swap vol=%s from dev=%s to %s\n", + VolumeName, vol->dev->print_name(), dev->print_name()); } else { - Dmsg4(dbglvl, "jid=%u Volume busy could not swap vol=%s from dev=%s to %s\n", - jid(), VolumeName, vol->dev->print_name(), dev->print_name()); - vol = NULL; /* device busy */ + Dmsg4(dbglvl, "jid=%u ==== Swap not possible Vol busy vol=%s from dev=%s to %s\n", jid(), + VolumeName, vol->dev->print_name(), dev->print_name()); + vol = NULL; /* device busy */ goto get_out; } + } else { + dev->vol = vol; } + } else { + dev->vol = vol; /* point to newly inserted volume */ } - dev->vol = vol; get_out: if (vol) { - Dmsg2(dbglvl, "jid=%u === set not released. vol=%s\n", jid(), vol->vol_name); - vol->released = false; + Dmsg3(dbglvl, "jid=%u === set in_use. vol=%s dev=%s\n", jid(), vol->vol_name, + vol->dev->print_name()); + vol->set_in_use(); + dcr->reserved_volume = true; } debug_list_volumes("end new volume"); unlock_volumes(); @@ -412,6 +433,7 @@ get_out: * Switch from current device to given device * (not yet used) */ +#ifdef xxx void switch_device(DCR *dcr, DEVICE *dev) { DCR save_dcr; @@ -431,11 +453,11 @@ void switch_device(DCR *dcr, DEVICE *dev) bstrncpy(dcr->pool_type, save_dcr.pool_type, sizeof(dcr->pool_type)); bstrncpy(dcr->dev_name, dev->dev_name, sizeof(dcr->dev_name)); - dev->reserved_device++; - dcr->reserved_device = true; +// dcr->set_reserved(); dev->dunlock(); } +#endif /* * Search for a Volume name in the Volume list. @@ -443,43 +465,56 @@ void switch_device(DCR *dcr, DEVICE *dev) * Returns: VOLRES entry on success * NULL if the Volume is not in the list */ -VOLRES *find_volume(DCR *dcr) +VOLRES *find_volume(const char *VolumeName) { VOLRES vol, *fvol; /* Do not lock reservations here */ lock_volumes(); - vol.vol_name = bstrdup(dcr->VolumeName); + vol.vol_name = bstrdup(VolumeName); fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare); free(vol.vol_name); - Dmsg3(dbglvl, "jid=%u find_vol=%s found=%d\n", jid(), dcr->VolumeName, fvol!=NULL); + Dmsg3(dbglvl, "jid=%u find_vol=%s found=%d\n", jid(), VolumeName, fvol!=NULL); debug_list_volumes("find_volume"); unlock_volumes(); return fvol; } +void DCR::set_reserved() +{ + m_reserved = true; + Dmsg2(dbglvl, "Inc reserve=%d dev=%s\n", dev->num_reserved(), dev->print_name()); + dev->inc_reserved(); +} + +void DCR::clear_reserved() +{ + if (m_reserved) { + m_reserved = false; + dev->dec_reserved(); + Dmsg2(dbglvl, "Dec reserve=%d dev=%s\n", dev->num_reserved(), dev->print_name()); + } +} + /* * Remove any reservation from a drive and tell the system * that the volume is unused at least by us. */ -void unreserve_device(DCR *dcr) +void DCR::unreserve_device() { - DEVICE *dev = dcr->dev; lock_volumes(); - if (dcr->reserved_device) { - dcr->reserved_device = false; - dev->reserved_device--; - Dmsg3(dbglvl, "jid=%u Dec reserve=%d dev=%s\n", jid(), dev->reserved_device, dev->print_name()); - dcr->reserved_device = false; + if (is_reserved()) { + clear_reserved(); + reserved_volume = false; /* If we set read mode in reserving, remove it */ if (dev->can_read()) { dev->clear_read(); } if (dev->num_writers < 0) { - Jmsg1(dcr->jcr, M_ERROR, 0, _("Hey! num_writers=%d!!!!\n"), dev->num_writers); + Jmsg1(jcr, M_ERROR, 0, _("Hey! num_writers=%d!!!!\n"), dev->num_writers); dev->num_writers = 0; } - if (dev->reserved_device == 0 && dev->num_writers == 0) { - volume_unused(dcr); + if (dev->num_reserved() == 0 && dev->num_writers == 0) { + volume_unused(this); } } unlock_volumes(); @@ -489,7 +524,7 @@ void unreserve_device(DCR *dcr) * Free a Volume from the Volume list if it is no longer used * Note, for tape drives we want to remember where the Volume * was when last used, so rather than free the volume entry, - * we simply mark it "released" so when the drive is really + * we simply mark it "not reserved" so when the drive is really * needed for another volume, we can reuse it. * * Returns: true if the Volume found and "removed" from the list @@ -499,21 +534,26 @@ bool volume_unused(DCR *dcr) { DEVICE *dev = dcr->dev; - if (dev->vol == NULL) { + if (!dev->vol) { Dmsg2(dbglvl, "jid=%u vol_unused: no vol on %s\n", (int)dcr->jcr->JobId, dev->print_name()); debug_list_volumes("null vol cannot unreserve_volume"); return false; } + if (dev->vol->is_swapping()) { + Dmsg1(dbglvl, "vol_unused: vol being swapped on %s\n", dev->print_name()); + debug_list_volumes("swapping vol cannot unreserve_volume"); + return false; + } #ifdef xxx if (dev->is_busy()) { - Dmsg2(dbglvl, "jid=%u vol_unused: no vol on %s\n", (int)dcr->jcr->JobId, dev->print_name()); + Dmsg2(dbglvl, "jid=%u vol_unused: busy on %s\n", (int)dcr->jcr->JobId, dev->print_name()); debug_list_volumes("dev busy cannot unreserve_volume"); return false; } #endif #ifdef xxx - if (dev->num_writers > 0 || dev->reserved_device > 0) { + if (dev->num_writers > 0 || dev->num_reserved() > 0) { ASSERT(0); } #endif @@ -524,12 +564,14 @@ bool volume_unused(DCR *dcr) * explicitly read in this drive. This allows the SD to remember * where the tapes are or last were. */ - dev->vol->released = true; + Dmsg5(dbglvl, "jid=%u === set not reserved vol=%s num_writers=%d dev_reserved=%d dev=%s\n", + jid(), dev->vol->vol_name, dev->num_writers, dev->num_reserved(), dev->print_name()); + dev->vol->clear_in_use(); if (dev->is_tape() || dev->is_autochanger()) { return true; } else { /* - * Note, this frees the volume reservation entry, but the + * Note, this frees the volume reservation entry, but the * file descriptor remains open with the OS. */ return free_volume(dev); @@ -551,11 +593,11 @@ bool free_volume(DEVICE *dev) vol = dev->vol; dev->vol = NULL; vol_list->remove(vol); - Dmsg3(dbglvl, "jid=%u free_volume %s dev=%s\n", jid(), vol->vol_name, dev->print_name()); + Dmsg3(dbglvl, "jid=%u === free_volume %s dev=%s\n", jid(), vol->vol_name, dev->print_name()); free_vol_item(vol); debug_list_volumes("free_volume"); unlock_volumes(); - return vol != NULL; + return true; } @@ -592,38 +634,40 @@ void free_volume_list() unlock_volumes(); } -bool is_volume_in_use(DCR *dcr) +bool DCR::can_i_use_volume() { - bool rtn = false; + bool rtn = true; VOLRES *vol; lock_volumes(); - vol = find_volume(dcr); + vol = find_volume(VolumeName); if (!vol) { - Dmsg2(dbglvl, "jid=%u Vol=%s not in use.\n", jid(), dcr->VolumeName); + Dmsg2(dbglvl, "jid=%u Vol=%s not in use.\n", jid(), VolumeName); goto get_out; /* vol not in list */ } ASSERT(vol->dev != NULL); - if (dcr->dev == vol->dev) { /* same device OK */ - Dmsg2(dbglvl, "jid=%u Vol=%s on same dev.\n", jid(), dcr->VolumeName); + if (dev == vol->dev) { /* same device OK */ + Dmsg2(dbglvl, "jid=%u Vol=%s on same dev.\n", jid(), VolumeName); goto get_out; } else { - Dmsg4(dbglvl, "jid=%u Vol=%s on %s we have %s\n", jid(), dcr->VolumeName, - vol->dev->print_name(), dcr->dev->print_name()); + Dmsg4(dbglvl, "jid=%u Vol=%s on %s we have %s\n", jid(), VolumeName, + vol->dev->print_name(), dev->print_name()); } + /* ***FIXME*** check this ... */ if (!vol->dev->is_busy()) { - Dmsg3(dbglvl, "jid=%u Vol=%s dev=%s not busy.\n", jid(), dcr->VolumeName, vol->dev->print_name()); + Dmsg3(dbglvl, "jid=%u Vol=%s dev=%s not busy.\n", jid(), VolumeName, vol->dev->print_name()); goto get_out; } else { - Dmsg3(dbglvl, "jid=%u Vol=%s dev=%s busy.\n", jid(), dcr->VolumeName, vol->dev->print_name()); + Dmsg3(dbglvl, "jid=%u Vol=%s dev=%s busy.\n", jid(), VolumeName, vol->dev->print_name()); } - Dmsg3(dbglvl, "jid=%u Vol=%s in use by %s.\n", jid(), dcr->VolumeName, vol->dev->print_name()); - rtn = true; + Dmsg3(dbglvl, "jid=%u Vol=%s in use by %s.\n", jid(), VolumeName, vol->dev->print_name()); + rtn = false; get_out: unlock_volumes(); return rtn; + } @@ -752,10 +796,6 @@ static bool use_storage_cmd(JCR *jcr) rctx.PreferMountedVols = false; rctx.exact_match = false; rctx.autochanger_only = true; - Dmsg6(dbglvl, "jid=%u PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n", - (int)rctx.jcr->JobId, - rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device, - rctx.autochanger_only, rctx.any_drive); if ((ok = find_suitable_device_for_job(jcr, rctx))) { break; } @@ -768,10 +808,6 @@ static bool use_storage_cmd(JCR *jcr) rctx.try_low_use_drive = false; } rctx.autochanger_only = false; - Dmsg6(dbglvl, "jid=%u PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n", - (int)rctx.jcr->JobId, - rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device, - rctx.autochanger_only, rctx.any_drive); if ((ok = find_suitable_device_for_job(jcr, rctx))) { break; } @@ -784,28 +820,16 @@ static bool use_storage_cmd(JCR *jcr) rctx.PreferMountedVols = true; rctx.exact_match = true; rctx.autochanger_only = false; - Dmsg6(dbglvl, "jid=%u PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n", - (int)rctx.jcr->JobId, - rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device, - rctx.autochanger_only, rctx.any_drive); if ((ok = find_suitable_device_for_job(jcr, rctx))) { break; } /* Look for any mounted drive */ rctx.exact_match = false; - Dmsg6(dbglvl, "jid=%u PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n", - (int)rctx.jcr->JobId, - rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device, - rctx.autochanger_only, rctx.any_drive); if ((ok = find_suitable_device_for_job(jcr, rctx))) { break; } /* Try any drive */ rctx.any_drive = true; - Dmsg6(dbglvl, "jid=%u PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n", - (int)rctx.jcr->JobId, - rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device, - rctx.autochanger_only, rctx.any_drive); if ((ok = find_suitable_device_for_job(jcr, rctx))) { break; } @@ -872,17 +896,19 @@ static bool is_vol_in_autochanger(RCTX &rctx, VOLRES *vol) /* Find resource, and make sure we were able to open it */ if (strcmp(rctx.device_name, changer->hdr.name) == 0) { - Dmsg2(dbglvl, "jid=%u Found changer device %s\n", - (int)rctx.jcr->JobId, vol->dev->device->hdr.name); + Dmsg2(dbglvl, "jid=%u Found changer device %s\n", (int)rctx.jcr->JobId, vol->dev->device->hdr.name); return true; } - Dmsg2(dbglvl, "jid=%u Incorrect changer device %s\n", - (int)rctx.jcr->JobId, changer->hdr.name); + Dmsg2(dbglvl, "jid=%u Incorrect changer device %s\n", (int)rctx.jcr->JobId, changer->hdr.name); return false; } /* * Search for a device suitable for this job. + * Note, this routine sets sets rctx.suitable_device if any + * device exists within the SD. The device may not be actually + * useable. + * It also returns if it finds a useable device. */ bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx) { @@ -1034,22 +1060,25 @@ bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx) rctx.device_name = device_name; stat = search_res_for_device(rctx); if (stat == 1) { /* found available device */ - Dmsg2(dbglvl, "jid=%u available device found=%s\n", (int)rctx.jcr->JobId, - device_name); + Dmsg2(dbglvl, "jid=%u available device found=%s\n", (int)rctx.jcr->JobId,device_name); ok = true; break; } else if (stat == 0) { /* device busy */ - Dmsg2(dbglvl, "jid=%u Suitable device=%s, busy: not use\n", - (int)rctx.jcr->JobId, device_name); + Dmsg2(dbglvl, "jid=%u No usable device=%s, busy: not use\n", (int)rctx.jcr->JobId,device_name); } else { /* otherwise error */ - Dmsg1(dbglvl, "jid=%u No suitable device found.\n", (int)rctx.jcr->JobId); + Dmsg1(dbglvl, "jid=%u No usable device found.\n", (int)rctx.jcr->JobId); } } if (ok) { break; } } + if (ok) { + Dmsg1(dbglvl, "OK dev found. Vol=%s\n", rctx.VolumeName); + } else { + Dmsg0(dbglvl, "Leave find_suit_dev: no dev found.\n"); + } return ok; } @@ -1080,11 +1109,11 @@ int search_res_for_device(RCTX &rctx) if (rctx.store->append == SD_APPEND) { Dmsg3(dbglvl, "jid=%u Device %s reserved=%d for append.\n", (int)rctx.jcr->JobId, rctx.device->hdr.name, - rctx.jcr->dcr->dev->reserved_device); + rctx.jcr->dcr->dev->num_reserved()); } else { Dmsg3(dbglvl, "jid=%u Device %s reserved=%d for read.\n", (int)rctx.jcr->JobId, rctx.device->hdr.name, - rctx.jcr->read_dcr->dev->reserved_device); + rctx.jcr->read_dcr->dev->num_reserved()); } return stat; } @@ -1105,11 +1134,11 @@ int search_res_for_device(RCTX &rctx) if (rctx.store->append == SD_APPEND) { Dmsg3(dbglvl, "jid=%u Device %s reserved=%d for append.\n", (int)rctx.jcr->JobId, rctx.device->hdr.name, - rctx.jcr->dcr->dev->reserved_device); + rctx.jcr->dcr->dev->num_reserved()); } else { Dmsg3(dbglvl, "jid=%u Device %s reserved=%d for read.\n", (int)rctx.jcr->JobId, rctx.device->hdr.name, - rctx.jcr->read_dcr->dev->reserved_device); + rctx.jcr->read_dcr->dev->num_reserved()); } return stat; } @@ -1157,7 +1186,7 @@ static int reserve_device(RCTX &rctx) } rctx.suitable_device = true; - Dmsg2(dbglvl, "jid=%u try reserve %s\n", rctx.jcr->JobId, rctx.device->hdr.name); + Dmsg1(dbglvl, "try reserve %s\n", rctx.device->hdr.name); rctx.jcr->dcr = dcr = new_dcr(rctx.jcr, rctx.jcr->dcr, rctx.device->dev); if (!dcr) { BSOCK *dir = rctx.jcr->dir_bsock; @@ -1180,7 +1209,7 @@ static int reserve_device(RCTX &rctx) rctx.jcr->dcr = dcr; Dmsg6(dbglvl, "jid=%u Reserved=%d dev_name=%s mediatype=%s pool=%s ok=%d\n", (int)rctx.jcr->JobId, - dcr->dev->reserved_device, + dcr->dev->num_reserved(), dcr->dev_name, dcr->media_type, dcr->pool_name, ok); if (rctx.have_volume) { if (reserve_volume(dcr, rctx.VolumeName)) { @@ -1206,10 +1235,10 @@ static int reserve_device(RCTX &rctx) * non-used drive and our one and only volume is mounted * elsewhere, so we bail out and retry using that drive. */ - if (dcr->volume_in_use && !rctx.PreferMountedVols) { + if (dcr->found_in_use() && !rctx.PreferMountedVols) { rctx.PreferMountedVols = true; if (dcr->VolumeName[0]) { - unreserve_device(dcr); + dcr->unreserve_device(); } goto bail_out; } @@ -1226,7 +1255,7 @@ static int reserve_device(RCTX &rctx) */ if (dcr->dev->num_writers != 0) { if (dcr->VolumeName[0]) { - unreserve_device(dcr); + dcr->unreserve_device(); } goto bail_out; } @@ -1238,7 +1267,7 @@ static int reserve_device(RCTX &rctx) rctx.jcr->read_dcr = dcr; Dmsg6(dbglvl, "jid=%u Read reserved=%d dev_name=%s mediatype=%s pool=%s ok=%d\n", (int)rctx.jcr->JobId, - dcr->dev->reserved_device, + dcr->dev->num_reserved(), dcr->dev_name, dcr->media_type, dcr->pool_name, ok); } } @@ -1293,7 +1322,7 @@ static bool reserve_device_for_read(DCR *dcr) if (dev->is_busy()) { Dmsg5(dbglvl, "jid=%u Device %s is busy ST_READ=%d num_writers=%d reserved=%d.\n", (int)jcr->JobId, dev->print_name(), - dev->state & ST_READ?1:0, dev->num_writers, dev->reserved_device); + dev->state & ST_READ?1:0, dev->num_writers, dev->num_reserved()); Mmsg(jcr->errmsg, _("3602 JobId=%u device %s is busy (already reading/writing).\n"), jcr->JobId, dev->print_name()); queue_reserve_message(jcr); @@ -1303,10 +1332,7 @@ static bool reserve_device_for_read(DCR *dcr) dev->clear_append(); dev->set_read(); ok = true; - dev->reserved_device++; - Dmsg4(dbglvl, "jid=%u Inc reserve=%d dev=%s %p\n", (int)jcr->JobId, - dev->reserved_device, dev->print_name(), dev); - dcr->reserved_device = true; + dcr->set_reserved(); bail_out: dev->dunlock(); @@ -1316,7 +1342,7 @@ bail_out: /* * We reserve the device for appending by incrementing the - * reserved_device. We do virtually all the same work that + * num_reserved(). We do virtually all the same work that * is done in acquire_device_for_append(), but we do * not attempt to mount the device. This routine allows * the DIR to reserve multiple devices before *really* @@ -1357,8 +1383,7 @@ static bool reserve_device_for_append(DCR *dcr, RCTX &rctx) goto bail_out; } - Dmsg2(dbglvl, "jid=%u reserve_append device is %s\n", - (int)jcr->JobId, dev->print_name()); + Dmsg2(dbglvl, "jid=%u reserve_append device is %s\n", jid(), dev->print_name()); /* Now do detailed tests ... */ if (can_reserve_drive(dcr, rctx) != 1) { @@ -1366,10 +1391,7 @@ static bool reserve_device_for_append(DCR *dcr, RCTX &rctx) goto bail_out; } - dev->reserved_device++; - Dmsg4(dbglvl, "jid=%u Inc reserve=%d dev=%s %p\n", (int)jcr->JobId, dev->reserved_device, - dev->print_name(), dev); - dcr->reserved_device = true; + dcr->set_reserved(); ok = true; bail_out: @@ -1393,7 +1415,7 @@ static int is_pool_ok(DCR *dcr) Mmsg(jcr->errmsg, _( "3608 JobId=%u wants Pool=\"%s\" but have Pool=\"%s\" nreserve=%d on drive %s.\n"), (uint32_t)jcr->JobId, dcr->pool_name, dev->pool_name, - dev->reserved_device, dev->print_name()); + dev->num_reserved(), dev->print_name()); queue_reserve_message(jcr); Dmsg2(dbglvl, "failed: busy num_writers=0, reserved, pool=%s wanted=%s\n", dev->pool_name, dcr->pool_name); @@ -1408,14 +1430,14 @@ static bool is_max_jobs_ok(DCR *dcr) Dmsg5(dbglvl, "MaxJobs=%d Jobs=%d reserves=%d Status=%s Vol=%s\n", dcr->VolCatInfo.VolCatMaxJobs, - dcr->VolCatInfo.VolCatJobs, dev->reserved_device, + dcr->VolCatInfo.VolCatJobs, dev->num_reserved(), dcr->VolCatInfo.VolCatStatus, dcr->VolumeName); if (strcmp(dcr->VolCatInfo.VolCatStatus, "Recycle") == 0) { return true; } if (dcr->VolCatInfo.VolCatMaxJobs > 0 && dcr->VolCatInfo.VolCatMaxJobs <= - (dcr->VolCatInfo.VolCatJobs + dev->reserved_device)) { + (dcr->VolCatInfo.VolCatJobs + dev->num_reserved())) { /* Max Job Vols depassed or already reserved */ Mmsg(jcr->errmsg, _("3610 JobId=%u Volume max jobs exceeded on drive %s.\n"), (uint32_t)jcr->JobId, dev->print_name()); @@ -1462,14 +1484,14 @@ static int can_reserve_drive(DCR *dcr, RCTX &rctx) /* If he wants a free drive, but this one is busy, no go */ if (!rctx.PreferMountedVols && dev->is_busy()) { /* Save least used drive */ - if ((dev->num_writers + dev->reserved_device) < rctx.num_writers) { - rctx.num_writers = dev->num_writers + dev->reserved_device; + if ((dev->num_writers + dev->num_reserved()) < rctx.num_writers) { + rctx.num_writers = dev->num_writers + dev->num_reserved(); rctx.low_use_drive = dev; Dmsg3(dbglvl, "jid=%u set low use drive=%s num_writers=%d\n", (int)jcr->JobId, dev->print_name(), rctx.num_writers); } else { Dmsg2(dbglvl, "jid=%u not low use num_writers=%d\n", - (int)jcr->JobId, dev->num_writers+dev->reserved_device); + (int)jcr->JobId, dev->num_writers+dev->num_reserved()); } Dmsg1(dbglvl, "jid=%u failed: !prefMnt && busy.\n", jcr->JobId); Mmsg(jcr->errmsg, _("3605 JobId=%u wants free drive but device %s is busy.\n"), @@ -1507,12 +1529,11 @@ static int can_reserve_drive(DCR *dcr, RCTX &rctx) jcr->JobId, rctx.VolumeName, dev->VolHdr.VolumeName, dev->print_name()); queue_reserve_message(jcr); - Dmsg4(dbglvl, "jid=%u not OK: dev have=%s resvol=%s want=%s\n", - (int)jcr->JobId, dev->VolHdr.VolumeName, - dev->vol?dev->vol->vol_name:"*none*", rctx.VolumeName); + Dmsg4(dbglvl, "jid=%u not OK: dev have=%s resvol=%s want=%s\n", (int)jcr->JobId, + dev->VolHdr.VolumeName, dev->vol?dev->vol->vol_name:"*none*", rctx.VolumeName); return 0; } - if (is_volume_in_use(dcr)) { + if (!dcr->can_i_use_volume()) { return 0; /* fail if volume on another drive */ } } @@ -1534,20 +1555,20 @@ static int can_reserve_drive(DCR *dcr, RCTX &rctx) */ if (dev->num_writers == 0) { /* Now check if there are any reservations on the drive */ - if (dev->reserved_device) { + if (dev->num_reserved()) { return is_pool_ok(dcr); } else if (dev->can_append()) { if (is_pool_ok(dcr)) { return 1; } else { /* Changing pool, unload old tape if any in drive */ - Dmsg1(dbglvl, "jid=%u OK dev: num_writers=0, not reserved, pool change, unload changer\n", - (int)jcr->JobId); + Dmsg1(dbglvl, "jid=%u OK dev: num_writers=0, not reserved, pool change, unload changer\n", (int)jcr->JobId); + /* ***FIXME*** use set_unload() */ unload_autochanger(dcr, 0); } } /* Device is available but not yet reserved, reserve it for us */ - Dmsg2(dbglvl, "jid=%u OK Dev avail reserved %s\n", jcr->JobId, dev->print_name()); + Dmsg2(dbglvl, "jid=%u OK Dev avail reserved %s\n", (int)jcr->JobId, dev->print_name()); bstrncpy(dev->pool_name, dcr->pool_name, sizeof(dev->pool_name)); bstrncpy(dev->pool_type, dcr->pool_type, sizeof(dev->pool_type)); return 1; /* reserve drive */ diff --git a/bacula/src/stored/scan.c b/bacula/src/stored/scan.c index e69dd94bdb..c1d033cf07 100644 --- a/bacula/src/stored/scan.c +++ b/bacula/src/stored/scan.c @@ -1,16 +1,7 @@ -/* - * - * scan.c scan a directory (on a removable file) for a valid - * Volume name. If found, open the file for append. - * - * Kern Sibbald, MMVI - * - * Version $Id$ - */ /* Bacula® - The Network Backup Solution - Copyright (C) 2006-2006 Free Software Foundation Europe e.V. + Copyright (C) 2006-2007 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. @@ -34,6 +25,15 @@ (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ +/* + * + * scan.c scan a directory (on a removable file) for a valid + * Volume name. If found, open the file for append. + * + * Kern Sibbald, MMVI + * + * Version $Id$ + */ #include "bacula.h" #include "stored.h" diff --git a/bacula/src/stored/stored.h b/bacula/src/stored/stored.h index d29f34cd5e..c6d83b4d34 100644 --- a/bacula/src/stored/stored.h +++ b/bacula/src/stored/stored.h @@ -1,7 +1,7 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2000-2006 Free Software Foundation Europe e.V. + Copyright (C) 2000-2008 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. diff --git a/bacula/src/stored/stored_conf.h b/bacula/src/stored/stored_conf.h index f59986b01a..679ccfb58f 100644 --- a/bacula/src/stored/stored_conf.h +++ b/bacula/src/stored/stored_conf.h @@ -57,10 +57,11 @@ public: char *password; /* Director password */ char *address; /* Director IP address or zero */ - int monitor; /* Have only access to status and .status functions */ - int tls_enable; /* Enable TLS */ - int tls_require; /* Require TLS */ - int tls_verify_peer; /* TLS Verify Client Certificate */ + bool monitor; /* Have only access to status and .status functions */ + bool tls_authenticate; /* Authenticate with TLS */ + bool tls_enable; /* Enable TLS */ + bool tls_require; /* Require TLS */ + bool tls_verify_peer; /* TLS Verify Client Certificate */ char *tls_ca_certfile; /* TLS CA Certificate File */ char *tls_ca_certdir; /* TLS CA Certificate Directory */ char *tls_certfile; /* TLS Server Certificate File */ @@ -82,14 +83,16 @@ public: char *working_directory; /* working directory for checkpoints */ char *pid_directory; char *subsys_directory; + char *plugin_directory; /* Plugin directory */ char *scripts_directory; uint32_t max_concurrent_jobs; /* maximum concurrent jobs to run */ MSGS *messages; /* Daemon message handler */ utime_t heartbeat_interval; /* Interval to send hb to FD */ utime_t client_wait; /* Time to wait for FD to connect */ - int tls_enable; /* Enable TLS */ - int tls_require; /* Require TLS */ - int tls_verify_peer; /* TLS Verify Client Certificate */ + bool tls_authenticate; /* Authenticate with TLS */ + bool tls_enable; /* Enable TLS */ + bool tls_require; /* Require TLS */ + bool tls_verify_peer; /* TLS Verify Client Certificate */ char *tls_ca_certfile; /* TLS CA Certificate File */ char *tls_ca_certdir; /* TLS CA Certificate Directory */ char *tls_certfile; /* TLS Server Certificate File */ diff --git a/bacula/src/version.h b/bacula/src/version.h index 792800bff2..48d3ae8d98 100644 --- a/bacula/src/version.h +++ b/bacula/src/version.h @@ -3,9 +3,9 @@ */ #undef VERSION -#define VERSION "2.2.9-b7" -#define BDATE "14 April 2008" -#define LSMDATE "14Apr08" +#define VERSION "2.2.10-b1" +#define BDATE "15 April 2008" +#define LSMDATE "15Apr08" #define PROG_COPYRIGHT "Copyright (C) %d-2008 Free Software Foundation Europe e.V.\n" #define BYEAR "2008" /* year for copyright messages in progs */ @@ -46,7 +46,7 @@ #define TRACE_FILE 1 /* If this is set stdout will not be closed on startup */ -/* #define DEVELOPER 1 */ +#define DEVELOPER 1 /* * SMCHECK does orphaned buffer checking (memory leaks) diff --git a/bacula/technotes-2.1 b/bacula/technotes-2.1 index 8508c2f64a..bdba766de4 100644 --- a/bacula/technotes-2.1 +++ b/bacula/technotes-2.1 @@ -1,6 +1,58 @@ Technical notes on version 2.2 General: +16Apr08 +kes Permit multiple simultaneous restores -- experiment. +15Apr08 +kes Backport development stream SD reservation system changes. + See below: +=== +16Apr08 +kes Move final volume swapping code to DCR method and + call it from acquire_for_read(). +kes When wrong volume is mounted during read, unload_autochanger. +14Apr08 +kes Stop searching for Volumes in SD askdir if DIR returns the + same volume name twice in a row. +13Apr08 +kes Tweak the swap Volume from one drive to another code so that + it now seems to work. +kes Resolve several problems with recycling that broke the new + code (these problems are in 2.2.9 as well). +kes Remove the volume lock when calling the sysop code. +kes Don't mark a reserved volume with no writers as unused otherwise + jobs fail. +12Apr08 +kes Rework class structures for VOLRES, DCR, and DEVICE to make + the method names a bit more logical, and for more logically + handling the responsibilities. +11Apr08 +kes Remove redundant code in terminating the scheduler that just + causes a seg fault in many cases. +kes Cleanup releasing a volume and make it a DCR method. +kes Improve algorithm for detecting pre-reserved volume and + swapping volumes. +10Apr08 +kes Tweak volume swapping code so it works. +kes Correct name overloading in mount.c +kes Replace released flag in VOLRES with reserved and add access + methods for the VOLRES class. +kes Prevent volume from being released while being swapped. +kes Refactor parts of stored/mount.c +09Apr08 +ebl Add sanity checks for VolWriteTime and VolReadTime +ebl Take care of bad clock changes while computing VolWriteTime + and VolReadTime. This should fix or limit #1066 +kes Correct error string numbers in dird/catreq.c +kes Restructure reserving, acquiring, and mounting volumes. Calls to + autochanger are deferred for mount.c -- simplifes the code. +08Apr08 +kes Do not prune any running job. It just fails the job. +kes Lock the volumes when changing dev->reserved_device and marking + the volume unused otherwise the device can get reserved by + another job before the volume is released, thus blocking it. +=== + Beta Release Version 2.2.9-b7 14Apr08 kes Close bat console windows first to eliminate error message -- 2.39.2