--- /dev/null
+
+ This patch can be applied to version 3.0.0. It ensures that the list
+ of volumes to be read is created very early when starting the Job in
+ the SD (before the drive reservation) so that any Volume to be read
+ cannot also be chosen to append.
+
+ Apply it to version 3.0.0 with:
+
+ cd <bacula-source>
+ patch -p0 <3.0.0-read-vol-list.patch
+ ./configure <your-options>
+ make
+ ...
+ make install
+
+
+
+Index: src/dird/msgchan.c
+===================================================================
+--- src/dird/msgchan.c (revision 8699)
++++ src/dird/msgchan.c (working copy)
+@@ -144,10 +144,12 @@
+ }
+ #endif
+
++static char OKbootstrap[] = "3000 OK bootstrap\n";
++
+ /*
+ * Start a job with the Storage daemon
+ */
+-bool start_storage_daemon_job(JCR *jcr, alist *rstore, alist *wstore)
++bool start_storage_daemon_job(JCR *jcr, alist *rstore, alist *wstore, bool send_bsr)
+ {
+ bool ok = true;
+ STORE *storage;
+@@ -196,7 +198,7 @@
+ &jcr->VolSessionTime, &auth_key) != 3) {
+ Dmsg1(100, "BadJob=%s\n", sd->msg);
+ Jmsg(jcr, M_FATAL, 0, _("Storage daemon rejected Job command: %s\n"), sd->msg);
+- return 0;
++ return false;
+ } else {
+ jcr->sd_auth_key = bstrdup(auth_key);
+ Dmsg1(150, "sd_auth_key=%s\n", jcr->sd_auth_key);
+@@ -204,9 +206,14 @@
+ } else {
+ Jmsg(jcr, M_FATAL, 0, _("<stored: bad response to Job command: %s\n"),
+ sd->bstrerror());
+- return 0;
++ return false;
+ }
+
++ if (send_bsr && (!send_bootstrap_file(jcr, sd) ||
++ !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR))) {
++ return false;
++ }
++
+ /*
+ * We have two loops here. The first comes from the
+ * Storage = associated with the Job, and we need
+Index: src/dird/migrate.c
+===================================================================
+--- src/dird/migrate.c (revision 8699)
++++ src/dird/migrate.c (working copy)
+@@ -55,7 +55,6 @@
+
+ static const int dbglevel = 10;
+
+-static char OKbootstrap[] = "3000 OK bootstrap\n";
+ static int get_job_to_migrate(JCR *jcr);
+ struct idpkt;
+ static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
+@@ -353,15 +352,11 @@
+ ((STORE *)jcr->rstorage->first())->name());
+ return false;
+ }
+- if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage)) {
++ if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage, /*send_bsr*/true)) {
+ return false;
+ }
+ Dmsg0(150, "Storage daemon connection OK\n");
+
+- if (!send_bootstrap_file(jcr, sd) ||
+- !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
+- return false;
+- }
+
+ /*
+ * We re-update the job start record so that the start
+Index: src/dird/protos.h
+===================================================================
+--- src/dird/protos.h (revision 8699)
++++ src/dird/protos.h (working copy)
+@@ -155,7 +155,8 @@
+ /* msgchan.c */
+ extern bool connect_to_storage_daemon(JCR *jcr, int retry_interval,
+ int max_retry_time, int verbose);
+-extern bool start_storage_daemon_job(JCR *jcr, alist *rstore, alist *wstore);
++extern bool start_storage_daemon_job(JCR *jcr, alist *rstore, alist *wstore,
++ bool send_bsr=false);
+ extern bool start_storage_daemon_message_thread(JCR *jcr);
+ extern int bget_dirmsg(BSOCK *bs);
+ extern void wait_for_storage_daemon_termination(JCR *jcr);
+Index: src/dird/vbackup.c
+===================================================================
+--- src/dird/vbackup.c (revision 8699)
++++ src/dird/vbackup.c (working copy)
+@@ -50,8 +50,6 @@
+
+ static const int dbglevel = 10;
+
+-static char OKbootstrap[] = "3000 OK bootstrap\n";
+-
+ static bool create_bootstrap_file(JCR *jcr, POOLMEM *jobids);
+ void vbackup_cleanup(JCR *jcr, int TermCode);
+
+@@ -217,16 +215,11 @@
+ /*
+ * Now start a job with the Storage daemon
+ */
+- if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage)) {
++ if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage, /*send_bsr*/true)) {
+ return false;
+ }
+ Dmsg0(100, "Storage daemon connection OK\n");
+
+- if (!send_bootstrap_file(jcr, sd) ||
+- !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
+- return false;
+- }
+-
+ /*
+ * We re-update the job start record so that the start
+ * time is set after the run before job. This avoids
+Index: src/stored/fd_cmds.c
+===================================================================
+--- src/stored/fd_cmds.c (revision 8699)
++++ src/stored/fd_cmds.c (working copy)
+@@ -388,6 +388,8 @@
+ if (debug_level >= 10) {
+ dump_bsr(jcr->bsr, true);
+ }
++ /* If we got a bootstrap, we are reading, so create read volume list */
++ create_restore_volume_list(jcr);
+ ok = true;
+
+ bail_out:
+Index: src/stored/read.c
+===================================================================
+--- src/stored/read.c (revision 8699)
++++ src/stored/read.c (working copy)
+@@ -1,7 +1,7 @@
+ /*
+ Bacula® - The Network Backup Solution
+
+- Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
++ Copyright (C) 2000-2009 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.
+@@ -62,11 +62,8 @@
+ return false;
+ }
+
+-
+- create_restore_volume_list(jcr);
+ if (jcr->NumReadVolumes == 0) {
+ Jmsg(jcr, M_FATAL, 0, _("No Volume names found for restore.\n"));
+- free_restore_volume_list(jcr);
+ fd->fsend(FD_error);
+ return false;
+ }
+@@ -76,7 +73,6 @@
+
+ /* Ready device for reading */
+ if (!acquire_device_for_read(dcr)) {
+- free_restore_volume_list(jcr);
+ fd->fsend(FD_error);
+ return false;
+ }
+@@ -92,7 +88,6 @@
+ ok = false;
+ }
+
+- free_restore_volume_list(jcr);
+ Dmsg0(30, "Done reading.\n");
+ return ok;
+ }
+Index: src/stored/parse_bsr.c
+===================================================================
+--- src/stored/parse_bsr.c (revision 8699)
++++ src/stored/parse_bsr.c (working copy)
+@@ -1,7 +1,7 @@
+ /*
+ Bacula® - The Network Backup Solution
+
+- Copyright (C) 2002-2008 Free Software Foundation Europe e.V.
++ Copyright (C) 2002-2009 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.
+Index: src/stored/mac.c
+===================================================================
+--- src/stored/mac.c (revision 8699)
++++ src/stored/mac.c (working copy)
+@@ -84,8 +84,6 @@
+ }
+ Dmsg2(100, "read_dcr=%p write_dcr=%p\n", jcr->read_dcr, jcr->dcr);
+
+-
+- create_restore_volume_list(jcr);
+ if (jcr->NumReadVolumes == 0) {
+ Jmsg(jcr, M_FATAL, 0, _("No Volume names found for %s.\n"), Type);
+ goto bail_out;
+@@ -161,11 +159,8 @@
+ }
+ }
+
+- free_restore_volume_list(jcr);
+-
+ dir_send_job_status(jcr); /* update director */
+
+-
+ Dmsg0(30, "Done reading.\n");
+ jcr->end_time = time(NULL);
+ dequeue_messages(jcr); /* send any queued messages */
+Index: src/stored/vol_mgr.c
+===================================================================
+--- src/stored/vol_mgr.c (revision 8699)
++++ src/stored/vol_mgr.c (working copy)
+@@ -529,6 +529,7 @@
+ VOLRES vol, *fvol;
+
+ if (read_vol_list->empty()) {
++ Dmsg0(dbglvl, "find_read_vol: read_vol_list empty.\n");
+ return NULL;
+ }
+ /* Do not lock reservations here */
+Index: src/stored/job.c
+===================================================================
+--- src/stored/job.c (revision 8699)
++++ src/stored/job.c (working copy)
+@@ -371,6 +371,8 @@
+ free_bsr(jcr->bsr);
+ jcr->bsr = NULL;
+ }
++ /* Free any restore volume list created */
++ free_restore_volume_list(jcr);
+ if (jcr->RestoreBootstrap) {
+ unlink(jcr->RestoreBootstrap);
+ free_pool_memory(jcr->RestoreBootstrap);
+Index: src/stored/acquire.c
+===================================================================
+--- src/stored/acquire.c (revision 8699)
++++ src/stored/acquire.c (working copy)
+@@ -187,7 +187,7 @@
+ if (!dir_get_volume_info(dcr, GET_VOL_INFO_FOR_READ)) {
+ Dmsg2(150, "dir_get_vol_info failed for vol=%s: %s\n",
+ dcr->VolumeName, jcr->errmsg);
+- Jmsg1(jcr, M_WARNING, 0, "%s", jcr->errmsg);
++ Jmsg1(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
+ }
+ dev->set_load(); /* set to load volume */
+
+@@ -241,7 +241,7 @@
+ * error messages when nothing is mounted.
+ */
+ if (tape_previously_mounted) {
+- Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
++ Jmsg(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
+ }
+ goto default_path;
+ case VOL_NAME_ERROR:
+@@ -257,7 +257,7 @@
+ dev->set_load();
+ /* Fall through */
+ default:
+- Jmsg1(jcr, M_WARNING, 0, "%s", jcr->errmsg);
++ Jmsg1(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
+ default_path:
+ Dmsg0(50, "default path\n");
+ tape_previously_mounted = true;