From 4bc0dde91d96b026b76ceb1b0faf525185f909f5 Mon Sep 17 00:00:00 2001 From: Kern Sibbald Date: Sat, 3 Jan 2004 21:50:43 +0000 Subject: [PATCH] Allow purge of JobId from Vol + keep SD from looping requesting new Vol at end of Volume -- Phil's bug git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@983 91ce42f0-d328-0410-95d8-f526ca767f89 --- bacula/kernstodo | 3 +- bacula/src/dird/ua_cmds.c | 6 +- bacula/src/dird/ua_purge.c | 58 ++++++-- bacula/src/stored/mount.c | 297 +++++++++++++++++++------------------ 4 files changed, 201 insertions(+), 163 deletions(-) diff --git a/bacula/kernstodo b/bacula/kernstodo index 67a1263208..e63c615992 100644 --- a/bacula/kernstodo +++ b/bacula/kernstodo @@ -55,6 +55,8 @@ For 1.33 Testing/Documentation: - Add subsections to the Disaster Recovery index section. For 1.33 +- Volume "add"ed to Pool gets recycled in first use. VolBytes=0 +- Get rid of 0 dates in LastWritten, ... - Make Bacula "poll a drive". - Fix "llist jobid=xx" where no fileset or client exists. - Release SQLite 2.8.8 @@ -1056,4 +1058,3 @@ Done: (see kernsdone for more) gnome-console is built, but the binary and .conf are not being installed. - Permit Bacula and apcupsd donations (not done for apcupsd). - Fix Ctl-C crashing the Console (readline?). - diff --git a/bacula/src/dird/ua_cmds.c b/bacula/src/dird/ua_cmds.c index 37ba7ab57e..6b4455c5ef 100644 --- a/bacula/src/dird/ua_cmds.c +++ b/bacula/src/dird/ua_cmds.c @@ -359,11 +359,13 @@ static int cancel_cmd(UAContext *ua, char *cmd) for (i=1; iargc; i++) { if (strcasecmp(ua->argk[i], _("jobid")) == 0) { + uint32_t JobId; if (!ua->argv[i]) { break; } - if (!(jcr=get_jcr_by_id(atoi(ua->argv[i])))) { - bsendmsg(ua, _("JobId %d is not running.\n"), atoi(ua->argv[i])); + JobId = str_to_int64(ua->argv[i]); + if (!(jcr=get_jcr_by_id(JobId))) { + bsendmsg(ua, _("JobId %d is not running.\n"), JobId); return 1; } break; diff --git a/bacula/src/dird/ua_purge.c b/bacula/src/dird/ua_purge.c index c0ce011d35..19c7ed4cf1 100644 --- a/bacula/src/dird/ua_purge.c +++ b/bacula/src/dird/ua_purge.c @@ -35,8 +35,9 @@ #include "dird.h" /* Forward referenced functions */ -int purge_files_from_client(UAContext *ua, CLIENT *client); -int purge_jobs_from_client(UAContext *ua, CLIENT *client); +static int purge_files_from_client(UAContext *ua, CLIENT *client); +static int purge_jobs_from_client(UAContext *ua, CLIENT *client); + void purge_files_from_volume(UAContext *ua, MEDIA_DBR *mr ); int purge_jobs_from_volume(UAContext *ua, MEDIA_DBR *mr); void purge_files_from_job(UAContext *ua, JOB_DBR *jr); @@ -270,7 +271,7 @@ int purgecmd(UAContext *ua, char *cmd) * the JobIds meeting the prune conditions, then delete all File records * pointing to each of those JobIds. */ -int purge_files_from_client(UAContext *ua, CLIENT *client) +static int purge_files_from_client(UAContext *ua, CLIENT *client) { struct s_file_del_ctx del; char *query = (char *)get_pool_memory(PM_MESSAGE); @@ -280,11 +281,11 @@ int purge_files_from_client(UAContext *ua, CLIENT *client) memset(&cr, 0, sizeof(cr)); memset(&del, 0, sizeof(del)); - strcpy(cr.Name, client->hdr.name); + bstrncpy(cr.Name, client->hdr.name, sizeof(cr.Name)); if (!db_create_client_record(ua->jcr, ua->db, &cr)) { return 0; } - + bsendmsg(ua, _("Begin purging files for Client \"%s\"\n"), cr.Name); Mmsg(&query, select_jobsfiles_from_client, cr.ClientId); Dmsg1(050, "select sql=%s\n", query); @@ -326,7 +327,7 @@ int purge_files_from_client(UAContext *ua, CLIENT *client) db_sql_query(ua->db, query, NULL, (void *)NULL); Dmsg1(050, "Del sql=%s\n", query); } - bsendmsg(ua, _("%d Files for client %s purged from %s catalog.\n"), del.num_ids, + bsendmsg(ua, _("%d Files for client \"%s\" purged from %s catalog.\n"), del.num_ids, client->hdr.name, client->catalog->hdr.name); bail_out: @@ -347,7 +348,7 @@ bail_out: * the JobIds meeting the prune conditions, then delete the Job, * Files, and JobMedia records in that list. */ -int purge_jobs_from_client(UAContext *ua, CLIENT *client) +static int purge_jobs_from_client(UAContext *ua, CLIENT *client) { struct s_job_del_ctx del; char *query = (char *)get_pool_memory(PM_MESSAGE); @@ -362,6 +363,7 @@ int purge_jobs_from_client(UAContext *ua, CLIENT *client) return 0; } + bsendmsg(ua, _("Begin purging jobs from Client \"%s\"\n"), cr.Name); Mmsg(&query, select_jobs_from_client, cr.ClientId); Dmsg1(050, "select sql=%s\n", query); @@ -476,7 +478,7 @@ int purge_jobs_from_volume(UAContext *ua, MEDIA_DBR *mr) } if (cnt.count == 0) { - bsendmsg(ua, "There are no Jobs associated with Volume %s. Marking it purged.\n", + bsendmsg(ua, "There are no Jobs associated with Volume \"%s\". Marking it purged.\n", mr->VolumeName); if (!mark_media_purged(ua, mr)) { bsendmsg(ua, "%s", db_strerror(ua->db)); @@ -491,13 +493,26 @@ int purge_jobs_from_volume(UAContext *ua, MEDIA_DBR *mr) del.max_ids = MAX_DEL_LIST_LEN; } - del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids); + /* + * Check if he wants to purge a single jobid + */ + i = find_arg_with_value(ua, "jobid"); + if (i >= 0) { + del.JobId = (JobId_t *)malloc(sizeof(JobId_t)); + del.num_ids = 1; + del.JobId[0] = str_to_int64(ua->argv[i]); + } else { + /* + * Purge ALL JobIds + */ + del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids); - Mmsg(&query, "SELECT JobId FROM JobMedia WHERE MediaId=%d", mr->MediaId); - if (!db_sql_query(ua->db, query, file_delete_handler, (void *)&del)) { - bsendmsg(ua, "%s", db_strerror(ua->db)); - Dmsg0(050, "Count failed\n"); - goto bail_out; + Mmsg(&query, "SELECT JobId FROM JobMedia WHERE MediaId=%d", mr->MediaId); + if (!db_sql_query(ua->db, query, file_delete_handler, (void *)&del)) { + bsendmsg(ua, "%s", db_strerror(ua->db)); + Dmsg0(050, "Count failed\n"); + goto bail_out; + } } for (i=0; i < del.num_ids; i++) { @@ -518,9 +533,20 @@ int purge_jobs_from_volume(UAContext *ua, MEDIA_DBR *mr) del.num_del==1?"":"s", mr->VolumeName); /* If purged, mark it so */ - if (del.num_ids == del.num_del) { + cnt.count = 0; + Mmsg(&query, "SELECT count(*) FROM JobMedia WHERE MediaId=%d", mr->MediaId); + if (!db_sql_query(ua->db, query, count_handler, (void *)&cnt)) { + bsendmsg(ua, "%s", db_strerror(ua->db)); + Dmsg0(050, "Count failed\n"); + goto bail_out; + } + + if (cnt.count == 0) { + bsendmsg(ua, "There are no more Jobs associated with Volume \"%s\". Marking it purged.\n", + mr->VolumeName); if (!(stat = mark_media_purged(ua, mr))) { bsendmsg(ua, "%s", db_strerror(ua->db)); + goto bail_out; } } @@ -539,7 +565,7 @@ int mark_media_purged(UAContext *ua, MEDIA_DBR *mr) strcmp(mr->VolStatus, "Full") == 0 || strcmp(mr->VolStatus, "Used") == 0 || strcmp(mr->VolStatus, "Error") == 0) { - strcpy(mr->VolStatus, "Purged"); + bstrncpy(mr->VolStatus, "Purged", sizeof(mr->VolStatus)); if (!db_update_media_record(ua->jcr, ua->db, mr)) { return 0; } diff --git a/bacula/src/stored/mount.c b/bacula/src/stored/mount.c index 096a856397..fa7dd67f0f 100644 --- a/bacula/src/stored/mount.c +++ b/bacula/src/stored/mount.c @@ -30,7 +30,8 @@ #include "bacula.h" /* pull in global headers */ #include "stored.h" /* pull in Storage Deamon headers */ -/* Forward referenced functions */ +/* Forward referenced routines */ +static void mark_volume_in_error(JCR *jcr, DEVICE *dev); /* @@ -47,11 +48,17 @@ */ int mount_next_write_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, int release) { - int retry = 0, autochanger; - bool ask, recycle; + int retry = 0; + bool ask = false, recycle, autochanger; + int vol_label_status; Dmsg0(100, "Enter mount_next_volume()\n"); + /* + * Attempt to mount the next volume. If something non-fatal goes + * wrong, we come back here to re-try (new op messages, re-read + * Volume, ...) + */ mount_next_vol: if (retry++ > 5) { Jmsg(jcr, M_FATAL, 0, _("Too many errors trying to mount device %s.\n"), @@ -62,8 +69,8 @@ mount_next_vol: Jmsg(jcr, M_FATAL, 0, _("Job %d canceled.\n"), jcr->JobId); return 0; } - autochanger = 0; - recycle = ask = false; + autochanger = false; /* Assume no autochanger */ + recycle = false; if (release) { Dmsg0(100, "mount_next_volume release=1\n"); release_volume(jcr, dev); @@ -97,147 +104,139 @@ mount_next_vol: * It assumes that the device is not already in use! * */ - - dev->state &= ~(ST_APPEND|ST_READ|ST_EOT|ST_WEOT|ST_EOF); - for ( ;; ) { - int vol_label_status; - autochanger = autoload_device(jcr, dev, 1, NULL); - Dmsg1(100, "autoload_dev returns %d\n", autochanger); - - /* - * If we autochanged to correct Volume or (we have not just - * released the Volume AND we can automount) we go ahead - * and read the label. If there is no tape in the drive, - * we will err, recurse and ask the operator the next time. - */ - if (autochanger || (!release && dev_is_tape(dev) && dev_cap(dev, CAP_AUTOMOUNT))) { - ask = false; /* don't ask SYSOP this time */ - } - Dmsg2(100, "Ask=%d autochanger=%d\n", ask, autochanger); - release = 1; /* release next time if we "recurse" */ + autochanger = autoload_device(jcr, dev, 1, NULL); + Dmsg1(100, "autoload_dev returns %d\n", autochanger); + /* + * If we autochanged to correct Volume or (we have not just + * released the Volume AND we can automount) we go ahead + * and read the label. If there is no tape in the drive, + * we will err, recurse and ask the operator the next time. + */ + if (autochanger || (!release && dev_is_tape(dev) && dev_cap(dev, CAP_AUTOMOUNT))) { + ask = false; /* don't ask SYSOP this time */ + } + Dmsg2(100, "Ask=%d autochanger=%d\n", ask, autochanger); + release = true; /* release next time if we "recurse" */ - if (ask && !dir_ask_sysop_to_mount_next_volume(jcr, dev)) { - Dmsg0(100, "Error return ask_sysop ...\n"); - return 0; /* error return */ - } - Dmsg1(100, "want vol=%s\n", jcr->VolumeName); + if (ask && !dir_ask_sysop_to_mount_next_volume(jcr, dev)) { + Dmsg0(100, "Error return ask_sysop ...\n"); + return 0; /* error return */ + } + Dmsg1(100, "want vol=%s\n", jcr->VolumeName); - /* Open device */ - if (!(dev_state(dev, ST_OPENED))) { - int mode; - if (dev_cap(dev, CAP_STREAM)) { - mode = OPEN_WRITE_ONLY; - } else { - mode = OPEN_READ_WRITE; - } - if (open_dev(dev, jcr->VolCatInfo.VolCatName, mode) < 0) { - Jmsg2(jcr, M_FATAL, 0, _("Unable to open device %s. ERR=%s\n"), - dev_name(dev), strerror_dev(dev)); - return 0; - } - } + /* Open device */ + if (!(dev_state(dev, ST_OPENED))) { + int mode; + if (dev_cap(dev, CAP_STREAM)) { + mode = OPEN_WRITE_ONLY; + } else { + mode = OPEN_READ_WRITE; + } + if (open_dev(dev, jcr->VolCatInfo.VolCatName, mode) < 0) { + Jmsg2(jcr, M_FATAL, 0, _("Unable to open device %s. ERR=%s\n"), + dev_name(dev), strerror_dev(dev)); + return 0; + } + } - /* - * Now make sure we have the right tape mounted - */ + /* + * Now make sure we have the right tape mounted + */ read_volume: - /* - * If we are writing to a stream device, ASSUME the volume label - * is correct. - */ - if (dev_cap(dev, CAP_STREAM)) { - vol_label_status = VOL_OK; - create_volume_label(dev, jcr->VolumeName, "Default"); - dev->VolHdr.LabelType = PRE_LABEL; - } else { - vol_label_status = read_dev_volume_label(jcr, dev, block); - } + /* + * If we are writing to a stream device, ASSUME the volume label + * is correct. + */ + if (dev_cap(dev, CAP_STREAM)) { + vol_label_status = VOL_OK; + create_volume_label(dev, jcr->VolumeName, "Default"); + dev->VolHdr.LabelType = PRE_LABEL; + } else { + vol_label_status = read_dev_volume_label(jcr, dev, block); + } - Dmsg2(100, "dirVol=%s dirStat=%s\n", jcr->VolumeName, - jcr->VolCatInfo.VolCatStatus); - /* - * At this point, dev->VolCatInfo has what is in the drive, if anything, - * and jcr->VolCatInfo has what the Director wants. - */ - switch (vol_label_status) { - case VOL_OK: - Dmsg1(100, "Vol OK name=%s\n", jcr->VolumeName); - memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(jcr->VolCatInfo)); - recycle = strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0; - break; /* got a Volume */ - case VOL_NAME_ERROR: - VOLUME_CAT_INFO VolCatInfo; + Dmsg2(100, "dirVol=%s dirStat=%s\n", jcr->VolumeName, + jcr->VolCatInfo.VolCatStatus); + /* + * At this point, dev->VolCatInfo has what is in the drive, if anything, + * and jcr->VolCatInfo has what the Director wants. + */ + switch (vol_label_status) { + case VOL_OK: + Dmsg1(100, "Vol OK name=%s\n", jcr->VolumeName); + memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(jcr->VolCatInfo)); + recycle = strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0; + break; /* got a Volume */ + case VOL_NAME_ERROR: + VOLUME_CAT_INFO VolCatInfo; - Dmsg1(100, "Vol NAME Error Name=%s\n", jcr->VolumeName); - /* - * OK, we got a different volume mounted. First save the - * requested Volume info (jcr) structure, then query if - * this volume is really OK. If not, put back the desired - * volume name and continue. - */ - memcpy(&VolCatInfo, &jcr->VolCatInfo, sizeof(VolCatInfo)); - /* Check if this is a valid Volume in the pool */ - pm_strcpy(&jcr->VolumeName, dev->VolHdr.VolName); - if (!dir_get_volume_info(jcr, GET_VOL_INFO_FOR_WRITE)) { - Mmsg(&jcr->errmsg, _("Director wanted Volume \"%s\".\n" - " Current Volume \"%s\" not acceptable because:\n" - " %s"), - VolCatInfo.VolCatName, dev->VolHdr.VolName, - jcr->dir_bsock->msg); - /* Restore desired volume name, note device info out of sync */ - memcpy(&jcr->VolCatInfo, &VolCatInfo, sizeof(jcr->VolCatInfo)); - goto mount_error; - } - Dmsg1(100, "want new name=%s\n", jcr->VolumeName); - memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(dev->VolCatInfo)); - recycle = strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0; - break; /* got a Volume */ - /* - * At this point, we assume we have a blank tape mounted. + Dmsg1(100, "Vol NAME Error Name=%s\n", jcr->VolumeName); + /* + * OK, we got a different volume mounted. First save the + * requested Volume info (jcr) structure, then query if + * this volume is really OK. If not, put back the desired + * volume name and continue. */ - case VOL_NO_LABEL: - case VOL_IO_ERROR: - /* - * If permitted, we label the device, make sure we can do - * it by checking that the VolCatBytes is zero => not labeled, - * once the Volume is labeled we don't want to label another - * blank tape with the same name. For disk, we go ahead and - * label it anyway, because the OS insures that there is only - * one Volume with that name. - * As noted above, at this point jcr->VolCatInfo has what - * the Director wants and dev->VolCatInfo has info on the - * previous tape (or nothing). - */ - if (dev_cap(dev, CAP_LABEL) && (jcr->VolCatInfo.VolCatBytes == 0 || - (!dev_is_tape(dev) && strcmp(jcr->VolCatInfo.VolCatStatus, - "Recycle") == 0))) { - Dmsg0(100, "Create volume label\n"); - if (!write_volume_label_to_dev(jcr, (DEVRES *)dev->device, jcr->VolumeName, - jcr->pool_name)) { - Dmsg0(100, "!write_vol_label\n"); - goto mount_next_vol; - } - Dmsg0(100, "dir_update_vol_info. Set Append\n"); - /* Copy Director's info into the device info */ - memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(dev->VolCatInfo)); - dir_update_volume_info(jcr, dev, 1); /* indicate tape labeled */ - Jmsg(jcr, M_INFO, 0, _("Labeled new Volume \"%s\" on device %s.\n"), - jcr->VolumeName, dev_name(dev)); - goto read_volume; /* read label we just wrote */ - } - /* NOTE! Fall-through wanted. */ - case VOL_NO_MEDIA: - default: -mount_error: - /* Send error message */ - Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); + memcpy(&VolCatInfo, &jcr->VolCatInfo, sizeof(VolCatInfo)); + /* Check if this is a valid Volume in the pool */ + pm_strcpy(&jcr->VolumeName, dev->VolHdr.VolName); + if (!dir_get_volume_info(jcr, GET_VOL_INFO_FOR_WRITE)) { + Jmsg(jcr, M_WARNING, 0, _("Director wanted Volume \"%s\".\n" + " Current Volume \"%s\" not acceptable because:\n" + " %s"), + VolCatInfo.VolCatName, dev->VolHdr.VolName, + jcr->dir_bsock->msg); + /* Restore desired volume name, note device info out of sync */ + memcpy(&jcr->VolCatInfo, &VolCatInfo, sizeof(jcr->VolCatInfo)); ask = true; - /* was - goto ask_again; */ goto mount_next_vol; } - break; + Dmsg1(100, "want new name=%s\n", jcr->VolumeName); + memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(dev->VolCatInfo)); + recycle = strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0; + break; /* got a Volume */ + /* + * At this point, we assume we have a blank tape mounted. + */ + case VOL_NO_LABEL: + case VOL_IO_ERROR: + /* + * If permitted, we label the device, make sure we can do + * it by checking that the VolCatBytes is zero => not labeled, + * once the Volume is labeled we don't want to label another + * blank tape with the same name. For disk, we go ahead and + * label it anyway, because the OS insures that there is only + * one Volume with that name. + * As noted above, at this point jcr->VolCatInfo has what + * the Director wants and dev->VolCatInfo has info on the + * previous tape (or nothing). + */ + if (dev_cap(dev, CAP_LABEL) && (jcr->VolCatInfo.VolCatBytes == 0 || + (!dev_is_tape(dev) && strcmp(jcr->VolCatInfo.VolCatStatus, + "Recycle") == 0))) { + Dmsg0(100, "Create volume label\n"); + if (!write_volume_label_to_dev(jcr, (DEVRES *)dev->device, jcr->VolumeName, + jcr->pool_name)) { + Dmsg0(100, "!write_vol_label\n"); + goto mount_next_vol; + } + Dmsg0(100, "dir_update_vol_info. Set Append\n"); + /* Copy Director's info into the device info */ + memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(dev->VolCatInfo)); + dir_update_volume_info(jcr, dev, 1); /* indicate tape labeled */ + Jmsg(jcr, M_INFO, 0, _("Labeled new Volume \"%s\" on device %s.\n"), + jcr->VolumeName, dev_name(dev)); + goto read_volume; /* read label we just wrote */ + } + /* NOTE! Fall-through wanted. */ + case VOL_NO_MEDIA: + default: + /* Send error message */ + Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); + ask = true; + goto mount_next_vol; } /* @@ -322,6 +321,10 @@ mount_error: Jmsg(jcr, M_INFO, 0, _("Wrote label to prelabeled Volume \"%s\" on device %s\n"), jcr->VolumeName, dev_name(dev)); } + /* + * End writing real Volume label (from pre-labeled tape), or recycling + * the volume. + */ } else { /* @@ -335,11 +338,7 @@ mount_error: if (!eod_dev(dev)) { Jmsg(jcr, M_ERROR, 0, _("Unable to position to end of data %s. ERR=%s\n"), dev_name(dev), strerror_dev(dev)); - Jmsg(jcr, M_INFO, 0, _("Marking Volume \"%s\" in Error in Catalog.\n"), - jcr->VolumeName); - bstrncpy(dev->VolCatInfo.VolCatStatus, "Error", sizeof(dev->VolCatInfo.VolCatStatus)); - Dmsg0(100, "dir_update_vol_info. Set Error.\n"); - dir_update_volume_info(jcr, dev, 0); + mark_volume_in_error(jcr, dev); goto mount_next_vol; } /* *****FIXME**** we should do some checking for files too */ @@ -355,9 +354,7 @@ mount_error: Jmsg(jcr, M_ERROR, 0, _("I canot write on this volume because:\n\ The number of files mismatch! Volume=%u Catalog=%u\n"), dev_file(dev), dev->VolCatInfo.VolCatFiles); - bstrncpy(dev->VolCatInfo.VolCatStatus, "Error", sizeof(dev->VolCatInfo.VolCatStatus)); - Dmsg0(100, "dir_update_vol_info. Set Error.\n"); - dir_update_volume_info(jcr, dev, 0); + mark_volume_in_error(jcr, dev); goto mount_next_vol; } } @@ -372,7 +369,19 @@ The number of files mismatch! Volume=%u Catalog=%u\n"), return 1; } +static void mark_volume_in_error(JCR *jcr, DEVICE *dev) +{ + Jmsg(jcr, M_INFO, 0, _("Marking Volume \"%s\" in Error in Catalog.\n"), + jcr->VolumeName); + bstrncpy(dev->VolCatInfo.VolCatStatus, "Error", sizeof(dev->VolCatInfo.VolCatStatus)); + Dmsg0(100, "dir_update_vol_info. Set Error.\n"); + dir_update_volume_info(jcr, dev, 0); +} +/* + * If we are reading, we come here at the end of the tape + * and see if there are more volumes to be mounted. + */ int mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) { Dmsg2(90, "NumVolumes=%d CurVolume=%d\n", jcr->NumVolumes, jcr->CurVolume); @@ -399,7 +408,6 @@ int mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) */ void release_volume(JCR *jcr, DEVICE *dev) { - if (jcr->WroteVol) { Jmsg0(jcr, M_ERROR, 0, "Hey!!!!! WroteVol non-zero !!!!!\n"); } @@ -411,7 +419,8 @@ void release_volume(JCR *jcr, DEVICE *dev) memset(&dev->VolCatInfo, 0, sizeof(dev->VolCatInfo)); memset(&jcr->VolCatInfo, 0, sizeof(jcr->VolCatInfo)); memset(&dev->VolHdr, 0, sizeof(dev->VolHdr)); - dev->state &= ~ST_LABEL; /* label not yet read */ + /* Force re-read of label */ + dev->state &= ~(ST_LABEL|ST_READ|ST_APPEND); jcr->VolumeName[0] = 0; if ((dev->state & ST_OPENED) && -- 2.39.5