int retry = 0;
    
    Dmsg1(50, "jcr->dcr=%p\n", jcr->dcr);
-   dev->block(BST_DOING_ACQUIRE);
+   dev->dblock(BST_DOING_ACQUIRE);
 
    if (dev->num_writers > 0) {
       Jmsg2(jcr, M_FATAL, 0, _("Acquire read: num_writers=%d not zero. Job %d canceled.\n"), 
        *  which we do not use except for the dev info.
        */
       stat = search_res_for_device(rctx);
-      release_msgs(jcr);              /* release queued messages */
+      release_reserve_messages(jcr);         /* release queued messages */
       unlock_reservations();
       if (stat == 1) {
          DCR *ndcr = jcr->read_dcr;
-         dev->unblock(dev_unlocked);
+         dev->dunblock(dev_unlocked);
          detach_dcr_from_dev(dcr);    /* release old device */
          /* Copy important info from the new dcr */
          dev = dcr->dev = ndcr->dev; 
          attach_dcr_to_dev(dcr);
          ndcr->VolumeName[0] = 0;
          free_dcr(ndcr);
-         dev->block(BST_DOING_ACQUIRE); 
+         dev->dblock(BST_DOING_ACQUIRE); 
          Jmsg(jcr, M_INFO, 0, _("Media Type change.  New device %s chosen.\n"),
             dev->print_name());
          bstrncpy(dcr->VolumeName, vol->VolumeName, sizeof(dcr->VolumeName));
       Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name());
       dcr->reserved_device = false;
    }
-   dev->unblock(dev_locked);
+   dev->dunblock(dev_locked);
    Dmsg1(50, "jcr->dcr=%p\n", jcr->dcr);
    return ok;
 }
 
    init_device_wait_timers(dcr);
 
-   dev->block(BST_DOING_ACQUIRE);
+   dev->dblock(BST_DOING_ACQUIRE);
    Dmsg1(190, "acquire_append device is %s\n", dev->is_tape()?"tape":
         (dev->is_dvd()?"DVD":"disk"));
 
       Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name());
       dcr->reserved_device = false;
    }
-   dev->unblock(dev_locked);
+   dev->dunblock(dev_locked);
    return dcr;
 
 /*
       Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name());
       dcr->reserved_device = false;
    }
-   dev->unblock(dev_locked);
+   dev->dunblock(dev_locked);
    return NULL;
 }
 
    pthread_cond_broadcast(&dev->wait_next_vol);
    Dmsg1(100, "JobId=%u broadcast wait_device_release\n", (uint32_t)jcr->JobId);
    pthread_cond_broadcast(&wait_device_release);
-   dcr->dev_locked = false;              /* set no longer locked */
    dev->dunlock();
    if (jcr->read_dcr == dcr) {
       jcr->read_dcr = NULL;
 
-/*
- * Append code for Storage daemon
- *  Kern Sibbald, May MM
- *
- *  Version $Id$
- */
 /*
    Bacula® - The Network Backup Solution
 
    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
    Switzerland, email:ftf@fsfeurope.org.
 */
+/*
+ * Append code for Storage daemon
+ *  Kern Sibbald, May MM
+ *
+ *  Version $Id$
+ */
 
 #include "bacula.h"
 #include "stored.h"
 
    ds = fd_sock;
 
-   if (!bnet_set_buffer_size(ds, dcr->device->max_network_buffer_size, BNET_SETBUF_WRITE)) {
+   if (!ds->set_buffer_size(dcr->device->max_network_buffer_size, BNET_SETBUF_WRITE)) {
       set_jcr_job_status(jcr, JS_ErrorTerminated);
       Jmsg0(jcr, M_FATAL, 0, _("Unable to set network buffer size.\n"));
       return false;
    }
 
    /* Tell File daemon to send data */
-   if (!bnet_fsend(fd_sock, OK_data)) {
+   if (!fd_sock->fsend(OK_data)) {
       berrno be;
       Jmsg1(jcr, M_FATAL, 0, _("Network send error to FD. ERR=%s\n"),
             be.bstrerror(fd_sock->b_errno));
             break;                    /* end of data */
          }
          Jmsg1(jcr, M_FATAL, 0, _("Error reading data header from FD. ERR=%s\n"),
-               bnet_strerror(ds));
+               ds->bstrerror());
          ok = false;
          break;
       }
                if (!dir_update_file_attributes(dcr, &rec)) {
                   jcr->dir_bsock->clear_spooling();
                   Jmsg(jcr, M_FATAL, 0, _("Error updating file attributes. ERR=%s\n"),
-                     bnet_strerror(jcr->dir_bsock));
+                     jcr->dir_bsock->bstrerror());
                   ok = false;
                   break;
                }
       Dmsg1(650, "End read loop with FD. Stat=%d\n", n);
 
       if (is_bnet_error(ds)) {
-         Dmsg1(350, "Network read error from FD. ERR=%s\n", bnet_strerror(ds));
+         Dmsg1(350, "Network read error from FD. ERR=%s\n", ds->bstrerror());
          Jmsg1(jcr, M_FATAL, 0, _("Network error on data channel. ERR=%s\n"),
-               bnet_strerror(ds));
+               ds->bstrerror());
          ok = false;
          break;
       }
    set_jcr_job_status(jcr, ok?JS_Terminated:JS_ErrorTerminated);
 
    /* Terminate connection with FD */
-   bnet_fsend(ds, OK_append);
+   ds->fsend(OK_append);
    do_fd_commands(jcr);               /* finish dialog with FD */
 
 
 
-/*
- *
- *   block.c -- tape block handling functions
- *
- *              Kern Sibbald, March MMI
- *                 added BB02 format October MMII
- *
- *   Version $Id$
- *
- */
 /*
    Bacula® - The Network Backup Solution
 
    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
    Switzerland, email:ftf@fsfeurope.org.
 */
+/*
+ *
+ *   block.c -- tape block handling functions
+ *
+ *              Kern Sibbald, March MMI
+ *                 added BB02 format October MMII
+ *
+ *   Version $Id$
+ *
+ */
 
 
 #include "bacula.h"
 
  *     daemon. More complicated coding (double buffering, writer
  *     thread, ...) is left for a later version.
  *
- *     Unfortunately, I have had to add more and more complication
- *     to this code. This was not foreseen as noted above, and as
- *     a consequence has lead to something more contorted than is
- *     really necessary -- KES.  Note, this contortion has been
- *     corrected to a large extent by a rewrite (Apr MMI).
- *
  *   Version $Id$
  */
 
 
 #ifndef __DEV_H
 #define __DEV_H 1
 
-#ifdef SD_DEBUG_LOCK
-#define r_dlock() _r_dlock(__FILE__, __LINE__);      /* in device.c */
-#define r_dunlock() _r_dunlock(__FILE__, __LINE__);  /* in device.c */
-#define dlock() _dlock(__FILE__, __LINE__);          /* in device.c */
-#define dunlock() _dunlock(__FILE__, __LINE__);     /* in device.c */
-#endif
-
 #undef DCR                            /* used by Bacula */
 
-#define block_device(d, s) _block_device(__FILE__, __LINE__, (d), s)
-#define unblock_device(d) _unblock_device(__FILE__, __LINE__, (d))
-#define steal_device_lock(d, p, s) _steal_device_lock(__FILE__, __LINE__, (d), (p), s)
-#define give_back_device_lock(d, p) _give_back_device_lock(__FILE__, __LINE__, (d), (p))
-
 /* Return values from wait_for_sysop() */
 enum {
    W_ERROR = 1,
 #define ST_PART_SPOOLED    (1<<18)    /* spooling part */
 #define ST_FREESPACE_OK    (1<<19)    /* Have valid freespace for DVD */
 
-/* m_blocked states (mutually exclusive) */
-enum {
-   BST_NOT_BLOCKED = 0,               /* not blocked */
-   BST_UNMOUNTED,                     /* User unmounted device */
-   BST_WAITING_FOR_SYSOP,             /* Waiting for operator to mount tape */
-   BST_DOING_ACQUIRE,                 /* Opening/validating/moving tape */
-   BST_WRITING_LABEL,                 /* Labeling a tape */
-   BST_UNMOUNTED_WAITING_FOR_SYSOP,   /* User unmounted during wait for op */
-   BST_MOUNT                          /* Mount request */
-};
 
 /* Volume Catalog Information structure definition */
 struct VOLUME_CAT_INFO {
 };
 
 
-typedef struct s_steal_lock {
-   pthread_t  no_wait_id;             /* id of no wait thread */
-   int        dev_blocked;            /* state */
-   int        dev_prev_blocked;       /* previous blocked state */
-} bsteal_lock_t;
-
 class DEVRES;                        /* Device resource defined in stored_conf.h */
 
 class DCR; /* forward reference */
 class VOLRES; /* forward reference */
 
-/*
- * Used in unblock() call
- */
-enum {
-   dev_locked = true,
-   dev_unlocked = false
-};
 
 /*
  * Device structure definition. There is one of these for
    int num_waiting;                   /* number of threads waiting */
    int num_writers;                   /* number of writing threads */
    int reserved_device;               /* number of device reservations */
-
-   /* New access control in process of being implemented */
-// brwlock_t xlock;                   /* New mutual exclusion lock */
-
    int capabilities;                  /* capabilities mask */
    int state;                         /* state mask */
    int dev_errno;                     /* Our own errno */
    char *bstrerror(void) { return errmsg; };
    char *print_errmsg() { return errmsg; };
 
-#ifdef  SD_DEBUG_LOCK
-   void _r_dlock(const char *, int);      /* in device.c */
-   void _r_dunlock(const char *, int);    /* in device.c */
-   void _dlock(const char *, int);        /* in device.c */
-   void _dunlock(const char *, int);      /* in device.c */
-#else
-   void r_dlock();                        /* in device.c */
-   void r_dunlock() { dunlock(); }
-   void dlock() { P(m_mutex); } 
-   void dunlock() { V(m_mutex); } 
-#endif
 
    void clear_volhdr();          /* in dev.c */
-   void block(int why);          /* in locks.c */
-   void unblock(bool locked=false); /* in locks.c */
    void close();                 /* in dev.c */
    void close_part(DCR *dcr);    /* in dev.c */
    bool truncate(DCR *dcr);      /* in dev.c */
    bool update_pos(DCR *dcr);    /* in dev.c */
    bool update_freespace();      /* in dvd.c */
 
-   void set_blocked(int block) { m_blocked = block; };
-   int  blocked() const { return m_blocked; };
    uint32_t get_file() const { return file; };
-   uint32_t get_block() const { return block_num; };
-   const char *print_blocked() const; /* in dev.c */
-   bool is_blocked() const { return m_blocked != BST_NOT_BLOCKED; };
+   uint32_t get_block_num() const { return block_num; };
    int fd() const { return m_fd; };
 
+   /* 
+    * Locking and blocking calls
+    */
+#ifdef  SD_DEBUG_LOCK
+   void _r_dlock(const char *, int);      /* in lock.c */
+   void _r_dunlock(const char *, int);    /* in lock.c */
+   void _dlock(const char *, int);        /* in lock.c */
+   void _dunlock(const char *, int);      /* in lock.c */
+#else
+   void r_dlock();                        /* in lock.c */
+   void r_dunlock() { dunlock(); }
+   void dlock() { P(m_mutex); } 
+   void dunlock() { V(m_mutex); } 
+#endif
+   void dblock(int why);                  /* in lock.c */
+   void dunblock(bool locked=false);      /* in lock.c */
+   void set_blocked(int block) { m_blocked = block; };
+   int blocked() const { return m_blocked; };
+   bool is_blocked() const { return m_blocked != BST_NOT_BLOCKED; };
+   const char *print_blocked() const;     /* in dev.c */
+
 private:
    bool do_mount(int mount, int timeout);      /* in dev.c */
    void set_mode(int omode);                   /* in dev.c */
 
 
    free_record(rec);
    Dmsg2(150, "Leave write_session_label Block=%ud File=%ud\n",
-      dev->get_block(), dev->get_file());
+      dev->get_block_num(), dev->get_file());
    return true;
 }
 
 
 #endif
 
 
-void DEVICE::block(int why)
+/*
+ *
+ * The Storage daemon has three locking concepts that must be
+ *   understood:
+ *
+ *  1. dblock    blocking the device, which means that the device
+ *               is "marked" in use.  When setting and removing the
+                 block, the device is locked, but after dblock is
+                 called the device is unlocked.
+ *  2. dlock()   simple mutex that locks the device structure. A dlock
+ *               can be acquired while a device is blocked if it is not
+ *               locked.      
+ *  3. r_dlock   "recursive" dlock, when means that a dlock (mutex)
+ *               will be acquired on the device if it is not blocked
+ *               by some other thread. If the device was blocked by
+ *               the current thread, it will acquire the lock.
+ *               If some other thread has set a block on the device,
+ *               this call will wait until the device is unblocked.
+ *
+ *  A lock is normally set when modifying the device structure.
+ *  A r_lock is normally acquired when you want to block the device
+ *    i.e. it will wait until the device is not blocked.
+ *  A block is normally set during long operations like writing to
+ *    the device.
+ *  If you are writing the device, you will normally block and 
+ *    lock it.
+ *  A lock cannot be violated. No other thread can touch the
+ *    device while a lock is set.  
+ *  When a block is set, every thread accept the thread that set
+ *    the block will block if r_dlock is called.
+ *  A device can be blocked for multiple reasons, labeling, writing,
+ *    acquiring (opening) the device, waiting for the operator, unmounted,
+ *    ...
+ *  Under certain conditions the block that is set on a device can be    
+ *    stolen and the device can be used by another thread. For example,
+ *    a device is blocked because it is waiting for the operator to  
+ *    mount a tape.  The operator can then unmount the device, and label
+ *    a tape, re-mount it, give back the block, and the job will continue.
+ *
+ *
+ * Functions:
+ *
+ *   DEVICE::dlock()   does P(m_mutex)     (in dev.h)
+ *   DEVICE::dunlock() does V(m_mutex)
+ *
+ *   DEVICE::dblock(why)  does 
+ *                    r_dlock();         (recursive device lock)
+ *                    block_device(this, why) 
+ *                    r_dunlock()
+ *
+ *   DEVICE::dunblock does
+ *                    dlock()
+ *                    unblock_device()
+ *                    dunlock()
+ *
+ *   DEVICE::r_dlock() does recursive locking
+ *                    dlock()
+ *                    if blocked and not same thread that locked
+ *                       pthread_cond_wait
+ *                    leaves device locked 
+ *
+ *   DEVICE::r_dunlock()
+ *                    same as dunlock();
+ *
+ *   block_device() does  (must be locked and not blocked at entry)  
+ *                    set blocked status
+ *                    set our pid
+ *
+ *   unblock_device() does (must be blocked at entry)
+ *                        (locked on entry)
+ *                        (locked on exit)
+ *                    set unblocked status
+ *                    clear pid
+ *                    if waiting threads
+ *                       pthread_cond_broadcast
+ *
+ *   steal_device_lock() does (must be locked and blocked at entry)
+ *                    save status
+ *                    set new blocked status
+ *                    set new pid
+ *                    unlock()
+ *
+ *   give_back_device_lock() does (must be blocked but not locked)
+ *                    dlock()
+ *                    reset blocked status
+ *                    save previous blocked
+ *                    reset pid
+ *                    if waiting threads
+ *                       pthread_cond_broadcast
+ *
+ */
+
+
+void DEVICE::dblock(int why)
 {
    r_dlock();              /* need recursive lock to block */
    block_device(this, why);
    r_dunlock();
 }
 
-void DEVICE::unblock(bool locked)
+void DEVICE::dunblock(bool locked)
 {
    if (!locked) {
       dlock();
  */
 #ifdef SD_DEBUG_LOCK
 void DEVICE::_r_dlock(const char *file, int line)
-#else
-void DEVICE::r_dlock()
-#endif
 {
-   int stat;
-#ifdef SD_DEBUG_LOCK
    Dmsg4(sd_dbglvl+1, "r_dlock blked=%s from %s:%d JobId=%u\n", this->print_blocked(),
          file, line, get_jobid_from_tid());
 #else
-   Dmsg1(sd_dbglvl, "reclock blked=%s\n", this->print_blocked());
+void DEVICE::r_dlock()
+{
 #endif
+   int stat;
    this->dlock();   
    if (this->blocked() && !pthread_equal(this->no_wait_id, pthread_self())) {
       this->num_waiting++;             /* indicate that I am waiting */
       return "BST_UNMOUNTED_WAITING_FOR_SYSOP";
    case BST_MOUNT:
       return "BST_MOUNT";
+   case BST_DESPOOLING:
+      return "BST_DESPOOLING";
    default:
       return _("unknown blocked code");
    }
           (blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
    return stat;
 }
-
 
--- /dev/null
+/*
+   Bacula® - The Network Backup Solution
+
+   Copyright (C) 2000-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.
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version two of the GNU General Public
+   License as published by the Free Software Foundation and included
+   in the file LICENSE.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula® is a registered trademark of John Walker.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
+/*
+ * Definitions for locking and blocking functions in the SD
+ *
+ * Kern Sibbald, pulled out of dev.h June 2007
+ *
+ *   Version $Id$
+ *
+ */
+
+
+#ifndef __LOCK_H
+#define __LOCK_H 1
+
+#ifdef SD_DEBUG_LOCK
+#define r_dlock()   _r_dlock(__FILE__, __LINE__);    /* in lock.c */
+#define r_dunlock() _r_dunlock(__FILE__, __LINE__);  /* in lock.c */
+#define dlock()     _dlock(__FILE__, __LINE__);      /* in lock.c */
+#define dunlock()   _dunlock(__FILE__, __LINE__);    /* in lock.c */
+#endif
+
+#define block_device(d, s)          _block_device(__FILE__, __LINE__, (d), s)
+#define unblock_device(d)           _unblock_device(__FILE__, __LINE__, (d))
+#define steal_device_lock(d, p, s)  _steal_device_lock(__FILE__, __LINE__, (d), (p), s)
+#define give_back_device_lock(d, p) _give_back_device_lock(__FILE__, __LINE__, (d), (p))
+
+/* m_blocked states (mutually exclusive) */
+enum {
+   BST_NOT_BLOCKED = 0,               /* not blocked */
+   BST_UNMOUNTED,                     /* User unmounted device */
+   BST_WAITING_FOR_SYSOP,             /* Waiting for operator to mount tape */
+   BST_DOING_ACQUIRE,                 /* Opening/validating/moving tape */
+   BST_WRITING_LABEL,                 /* Labeling a tape */
+   BST_UNMOUNTED_WAITING_FOR_SYSOP,   /* User unmounted during wait for op */
+   BST_MOUNT,                         /* Mount request */
+   BST_DESPOOLING                     /* Despooling -- i.e. multiple writes */
+};
+
+typedef struct s_steal_lock {
+   pthread_t  no_wait_id;             /* id of no wait thread */
+   int        dev_blocked;            /* state */
+   int        dev_prev_blocked;       /* previous blocked state */
+} bsteal_lock_t;
+
+/*
+ * Used in unblock() call
+ */
+enum {
+   dev_locked = true,
+   dev_unlocked = false
+};
+
+#endif
 
 void    send_drive_reserve_messages(JCR *jcr, void sendit(const char *msg, int len, void *sarg), void *arg);
 bool    find_suitable_device_for_job(JCR *jcr, RCTX &rctx);
 int     search_res_for_device(RCTX &rctx);
-void    release_msgs(JCR *jcr);
+void    release_reserve_messages(JCR *jcr);
 
 extern int reservations_lock_count;
 extern int vol_list_lock_count;
 
 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);
 
 /* Requests from the Director daemon */
 static char use_storage[]  = "use storage=%127s media_type=%127s "
    int Copy, Stripe;
    DIRSTORE *store;
    RCTX rctx;
-   char *msg;
-   alist *msgs;
    alist *dirstore;
 
    memset(&rctx, 0, sizeof(RCTX));
     *   use_device for each device that it wants to use.
     */
    dirstore = New(alist(10, not_owned_by_alist));
-   msgs = jcr->reserve_msgs = New(alist(10, not_owned_by_alist));  
+   jcr->reserve_msgs = New(alist(10, not_owned_by_alist));  
    do {
       Dmsg2(dbglvl, "jid=%u <dird: %s", jid(), dir->msg);
       ok = sscanf(dir->msg, use_storage, store_name.c_str(), 
 
       lock_reservations();
       for ( ; !fail && !job_canceled(jcr); ) {
-         while ((msg = (char *)msgs->pop())) {
-            free(msg);
-         }
+         pop_reserve_messages(jcr);
          rctx.suitable_device = false;
          rctx.have_volume = false;
          rctx.VolumeName[0] = 0;
       Dmsg2(dbglvl, "jid=%u >dird: %s", jid(), dir->msg);
    }
 
-   release_msgs(jcr);
+   release_reserve_messages(jcr);
    return ok;
 }
 
    jcr->unlock();
 }
 
-void release_msgs(JCR *jcr)
+/*
+ * Pop and release any reservations messages
+ */
+static void pop_reserve_messages(JCR *jcr)
 {
    alist *msgs;
    char *msg;
    while ((msg = (char *)msgs->pop())) {
       free(msg);
    }
-   delete msgs;
+bail_out:
+   jcr->unlock();
+}
+
+/*
+ * Also called from acquire.c 
+ */
+void release_reserve_messages(JCR *jcr)
+{
+   pop_reserve_messages(jcr);
+   jcr->lock();
+   if (!jcr->reserve_msgs) {
+      goto bail_out;
+   }
+   delete jcr->reserve_msgs;
    jcr->reserve_msgs = NULL;
 
 bail_out:
 
 #   endif
 # endif
 #endif
+#include "lock.h"
 #include "block.h"
 #include "record.h"
 #include "dev.h"
 
 
 #undef  VERSION
 #define VERSION "2.1.23"
-#define BDATE   "26 June 2007"
-#define LSMDATE "26Jun07"
+#define BDATE   "28 June 2007"
+#define LSMDATE "28Jun07"
 
 #define PROG_COPYRIGHT "Copyright (C) %d-2007 Free Software Foundation Europe e.V.\n"
 #define BYEAR "2007"       /* year for copyright messages in progs */