General:
 
 Changes to 1.37.28:
+11Jul05
+- Lots of documentation.
+- Do not prune volume marked as append when needing a
+  new Volume.
+- Print a warning message in SD if a non-used Volume
+  is specified and autolabel not turned on.
+- Correct a bug in chksum.c concerning SHA1 signatures 
+  (an * should have been & when checking for a bit flag).
+- Print File:Block for all label records in label.c -- concerns
+  primarily bls when doing Job listings (-j).
+- Correct is_volume_in_use() to return false if testing
+  on the same device where the Volume is already mounted.
+- Define a init_done flag in the SD that is set when the
+  devices are initialized and make users connecting wait.
+  This prevents useless connect failure warning messages.
+- Do additional device locking in ask_op_to_mount_volume()
+  to prevent race conditions with a user labeling a Volume
+  or autolabeling.
 09Jul05
 - Add a test for error return from bnet_wait... in heartbeat.c
   in FD to avoid CPU loop.
 
  *
  *    Version $Id$
  */
-
 /*
    Copyright (C) 2000-2005 Kern Sibbald
 
    This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of
-   the License, or (at your option) any later version.
+   modify it under the terms of the GNU General Public License
+   version 2 as amended with additional clauses defined in the
+   file LICENSE in the main source directory.
 
    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., 59 Temple Place - Suite 330, Boston,
-   MA 02111-1307, USA.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
+   the file LICENSE for additional details.
 
  */
 
    db_lock(mdb);
    Mmsg(mdb->cmd, "SELECT MediaId FROM Media WHERE VolumeName='%s'",
            mr->VolumeName);
-   Dmsg1(300, "selectpool: %s\n", mdb->cmd);
+   Dmsg1(500, "selectpool: %s\n", mdb->cmd);
 
    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
       mdb->num_rows = sql_num_rows(mdb);
 "VolStatus,Slot,VolBytes,InChanger,VolReadTime,VolWriteTime,VolParts,"
 "EndFile,EndBlock,LabelType,StorageId) "
 "VALUES ('%s','%s',%u,%s,%s,%d,%s,%s,%u,%u,'%s',%d,%s,%d,%s,%s,%d,0,0,%d,%s)",
-                  mr->VolumeName,
-                  mr->MediaType, mr->PoolId,
-                  edit_uint64(mr->MaxVolBytes,ed1),
-                  edit_uint64(mr->VolCapacityBytes, ed2),
-                  mr->Recycle,
-                  edit_uint64(mr->VolRetention, ed3),
-                  edit_uint64(mr->VolUseDuration, ed4),
-                  mr->MaxVolJobs,
-                  mr->MaxVolFiles,
-                  mr->VolStatus,
-                  mr->Slot,
-                  edit_uint64(mr->VolBytes, ed5),
-                  mr->InChanger,
-                  edit_uint64(mr->VolReadTime, ed6),
-                  edit_uint64(mr->VolWriteTime, ed7),
-                  mr->VolParts,
-                  mr->LabelType,
-                  edit_int64(mr->StorageId, ed8) 
-                  );
+          mr->VolumeName,
+          mr->MediaType, mr->PoolId,
+          edit_uint64(mr->MaxVolBytes,ed1),
+          edit_uint64(mr->VolCapacityBytes, ed2),
+          mr->Recycle,
+          edit_uint64(mr->VolRetention, ed3),
+          edit_uint64(mr->VolUseDuration, ed4),
+          mr->MaxVolJobs,
+          mr->MaxVolFiles,
+          mr->VolStatus,
+          mr->Slot,
+          edit_uint64(mr->VolBytes, ed5),
+          mr->InChanger,
+          edit_uint64(mr->VolReadTime, ed6),
+          edit_uint64(mr->VolWriteTime, ed7),
+          mr->VolParts,
+          mr->LabelType,
+          edit_int64(mr->StorageId, ed8) 
+          );
 
 
    Dmsg1(500, "Create Volume: %s\n", mdb->cmd);
 
 /*
  *
  *   Bacula Director -- Automatic Pruning
- *     Applies retention periods
+ *      Applies retention periods
  *
  *     Kern Sibbald, May MMII
  *
    CLIENT *client;
    bool pruned;
 
-   if (!jcr->client) {               /* temp -- remove me */
+   if (!jcr->client) {                /* temp -- remove me */
       return 1;
    }
 
  *   volume and no appendable volumes are available.
  *
  *  Return 0: on error
- *        number of Volumes Purged
+ *         number of Volumes Purged
  */
 int prune_volumes(JCR *jcr)
 {
    for (i=0; i<num_ids; i++) {
       mr.MediaId = ids[i];
       if (!db_get_media_record(jcr, jcr->db, &mr)) {
-        Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
-        continue;
+         Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
+         continue;
       }
       /* Prune only Volumes from current Pool */
       if (jcr->PoolId != mr.PoolId) {
-        continue;
+         continue;
       }
-      /* Prune only Volumes with status "Full", "Used", or "Append" */
+      /* Prune only Volumes with status "Full", or "Used" */
       if (strcmp(mr.VolStatus, "Full")   == 0 ||
-         strcmp(mr.VolStatus, "Append") == 0 ||
-         strcmp(mr.VolStatus, "Used")   == 0) {
-        Dmsg1(200, "Prune Volume %s\n", mr.VolumeName);
-        stat += prune_volume(ua, &mr);
-        Dmsg1(200, "Num pruned = %d\n", stat);
+          strcmp(mr.VolStatus, "Used")   == 0) {
+         Dmsg1(200, "Prune Volume %s\n", mr.VolumeName);
+         stat += prune_volume(ua, &mr);
+         Dmsg1(200, "Num pruned = %d\n", stat);
       }
    }
 
 
    }
 
    if (cnt.count == 0) {
+      /* Don't mark appendable volume as purged */
+      if (strcmp(mr->VolStatus, "Append") == 0 && verbose) {
+         bsendmsg(ua, "There are no Jobs associated with Volume \"%s\". Prune not needed.\n",
+            mr->VolumeName);
+         stat = 1;
+         goto bail_out;
+      }
+      /* If volume not already purged, do so */
       if (strcmp(mr->VolStatus, "Purged") != 0 && verbose) {
          bsendmsg(ua, "There are no Jobs associated with Volume \"%s\". Marking it purged.\n",
             mr->VolumeName);
 
  *
  *   Version $Id$
  */
+/*
+   Copyright (C) 2004-2005 Kern Sibbald
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   version 2 as amended with additional clauses defined in the
+   file LICENSE in the main source directory.
+
+   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 
+   the file LICENSE for additional details.
+
+ */
 
 #ifndef TEST_PROGRAM
 
 #include "acl.h"
 
 #define BACLLEN 65535
-#define pm_strcpy(d,s)    (strncpy(d, s, BACLLEN - 1) == NULL ? -1 : (int)strlen(d))
-#define Dmsg0(n,s)        fprintf(stderr, s)
-#define Dmsg1(n,s,a1)     fprintf(stderr, s, a1)
+#define pm_strcpy(d,s)     (strncpy(d, s, BACLLEN - 1) == NULL ? -1 : (int)strlen(d))
+#define Dmsg0(n,s)         fprintf(stderr, s)
+#define Dmsg1(n,s,a1)      fprintf(stderr, s, a1)
 #define Dmsg2(n,s,a1,a2)   fprintf(stderr, s, a1, a2)
 
 int aclls(char *fname);
 
 /* On IRIX we can get shortened ACLs */
 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
-#define acl_to_text(acl,len)    acl_to_short_text((acl), (len))
+#define acl_to_text(acl,len)     acl_to_short_text((acl), (len))
 #endif
 
 /* In Linux we can get numeric and/or shorted ACLs */
 #if defined(HAVE_LINUX_OS)
 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
-#define BACL_ALTERNATE_TEXT           (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
+#define BACL_ALTERNATE_TEXT            (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
 #elif defined(BACL_WANT_SHORT_ACLS)
-#define BACL_ALTERNATE_TEXT           TEXT_ABBREVIATE
+#define BACL_ALTERNATE_TEXT            TEXT_ABBREVIATE
 #elif defined(BACL_WANT_NUMERIC_IDS)
-#define BACL_ALTERNATE_TEXT           TEXT_NUMERIC_IDS
+#define BACL_ALTERNATE_TEXT            TEXT_NUMERIC_IDS
 #endif
 #ifdef BACL_ALTERNATE_TEXT
 #include <acl/libacl.h>
    acl = acl_get_file(jcr->last_fname, ostype);
    if (acl) {
       if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
-        len = pm_strcpy(jcr->acl_text, acl_text);
-        acl_free(acl);
-        acl_free(acl_text);
-        return len;
+         len = pm_strcpy(jcr->acl_text, acl_text);
+         acl_free(acl);
+         acl_free(acl_text);
+         return len;
       }
       acl_free(acl);
-#ifndef HAVE_OSF1_OS         /* BACL_ENOTSUP not defined for OSF1 */
+#ifndef HAVE_OSF1_OS          /* BACL_ENOTSUP not defined for OSF1 */
    } else if (errno == BACL_ENOTSUP) {
       /* Not supported, just pretend there is nothing to see */
       return pm_strcpy(jcr->acl_text, "");
    /* If we get empty default ACLs, clear ACLs now */
    if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_text) == 0) {
       if (acl_delete_def_file(jcr->last_fname) == 0) {
-        return 0;
+         return 0;
       }
       return -1;
    }
 
    if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
       if (errno == BACL_ENOTSUP) {
-        return pm_strcpy(jcr->acl_text, "");
+         return pm_strcpy(jcr->acl_text, "");
       }
       return -1;
    }
    if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
       if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
-        len = pm_strcpy(jcr->acl_text, acl_text);
-        free(acl_text);
-        return len;
+         len = pm_strcpy(jcr->acl_text, acl_text);
+         free(acl_text);
+         return len;
       }
    }
    return -1;
    }
    if (acl(jcr->last_fname, GETACL, n, acls) == n) {
       if ((acl_text = acltotext(acls, n)) != NULL) {
-        len = pm_strcpy(jcr->acl_text, acl_text);
-        free(acl_text);
-        free(acls);
-        return len;
+         len = pm_strcpy(jcr->acl_text, acl_text);
+         free(acl_text);
+         free(acls);
+         return len;
       }
    }
    free(acls);
    if (strcmp(prgname, "aclcp") == 0) {
       int verbose = 0;
       if (strcmp(*argv, "-v") == 0) {
-        ++verbose;
-        --argc;
-        ++argv;
+         ++verbose;
+         --argc;
+         ++argv;
       }
       if (argc != 2) {
          Dmsg2(200, "%s: wrong number of arguments\n"
                "usage:\t%s [-v] source destination\n"
                "\tCopies ACLs from source to destination.\n"
                "\tSpecify -v to show ACLs after copy for verification.\n",
-              prgname, prgname);
-        return EXIT_FAILURE;
+               prgname, prgname);
+         return EXIT_FAILURE;
       }
       if (strcmp(argv[0], argv[1]) == 0) {
          Dmsg2(200, "%s: identical source and destination.\n"
                "usage:\t%s [-v] source destination\n"
                "\tCopies ACLs from source to destination.\n"
                "\tSpecify -v to show ACLs after copy for verification.\n",
-              prgname, prgname);
-        return EXIT_FAILURE;
+               prgname, prgname);
+         return EXIT_FAILURE;
       }
       if (verbose) {
-        aclls(argv[0]);
+         aclls(argv[0]);
       }
       status = aclcp(argv[0], argv[1]);
       if (verbose && status == 0) {
-        aclls(argv[1]);
+         aclls(argv[1]);
       }
       return status;
    }
       Dmsg2(200, "%s: missing arguments\n"
             "usage:\t%s file ...\n"
             "\tLists ACLs of specified files or directories.\n",
-           prgname, prgname);
+            prgname, prgname);
       return EXIT_FAILURE;
    }
    while (argc--) {
       if (!aclls(*argv++)) {
-        status = EXIT_FAILURE;
+         status = EXIT_FAILURE;
       }
    }
 
       jcr.last_fname = dst;
       if (bacl_set(&jcr, BACL_TYPE_ACCESS) < 0) {
          Dmsg1(200, "aclcp: could not set ACLs on %s\n", jcr.last_fname);
-        return EXIT_FAILURE;
+         return EXIT_FAILURE;
       }
    }
 
       jcr.last_fname = src;
       if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
          Dmsg1(200, "aclcp: could not read default ACLs for %s\n", jcr.last_fname);
-        return EXIT_FAILURE;
+         return EXIT_FAILURE;
       } else {
-        jcr.last_fname = dst;
-        if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
+         jcr.last_fname = dst;
+         if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
             Dmsg1(200, "aclcp: could not set default ACLs on %s\n", jcr.last_fname);
-           return EXIT_FAILURE;
-        }
+            return EXIT_FAILURE;
+         }
       }
    }
 
       len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
       if (len < 0) {
          Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
-        return EXIT_FAILURE;
+         return EXIT_FAILURE;
       } else if (len == 0) {
-        printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
+         printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
       } else {
-        printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);
+         printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);
       }
    }
 
 
    Copyright (C) 2000-2005 Kern Sibbald
 
    This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of
-   the License, or (at your option) any later version.
+   modify it under the terms of the GNU General Public License
+   version 2 as amended with additional clauses defined in the
+   file LICENSE in the main source directory.
 
    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., 59 Temple Place - Suite 330, Boston,
-   MA 02111-1307, USA.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
+   the file LICENSE for additional details.
 
  */
 
 
  *
  *  Written by Preben 'Peppe' Guldberg, December MMIV
  */
-
 /*
-   Copyright (C) 2004 Kern Sibbald
+   Copyright (C) 2004-2005 Kern Sibbald
 
    This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of
-   the License, or (at your option) any later version.
+   modify it under the terms of the GNU General Public License
+   version 2 as amended with additional clauses defined in the
+   file LICENSE in the main source directory.
 
    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., 59 Temple Place - Suite 330, Boston,
-   MA 02111-1307, USA.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
+   the file LICENSE for additional details.
 
  */
 
       MD5Init(&chksum->context.md5);
       chksum->type = CHKSUM_MD5;
       bstrncpy(chksum->name, "MD5", sizeof(chksum->name));
-   } else if (flags * CHKSUM_SHA1) {
+   } else if (flags & CHKSUM_SHA1) {
       chksum->length = 20;
       status = SHA1Init(&chksum->context.sha1);
       if (status == 0) {
-        chksum->type = CHKSUM_SHA1;
-        bstrncpy(chksum->name, "SHA1", sizeof(chksum->name));
+         chksum->type = CHKSUM_SHA1;
+         bstrncpy(chksum->name, "SHA1", sizeof(chksum->name));
       }
    }
    return status;
    case CHKSUM_SHA1:
       status = SHA1Update(&chksum->context.sha1, (uint8_t *)buf, len);
       if (status == 0) {
-        chksum->updated = true;
+         chksum->updated = true;
       }
       return status;
    default:
 
 /*
  * General routines for handling the various checksum supported.
  */
-
 /*
-   Copyright (C) 2004 Kern Sibbald
+   Copyright (C) 2000-2005 Kern Sibbald
 
    This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of
-   the License, or (at your option) any later version.
+   modify it under the terms of the GNU General Public License
+   version 2 as amended with additional clauses defined in the
+   file LICENSE in the main source directory.
 
    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., 59 Temple Place - Suite 330, Boston,
-   MA 02111-1307, USA.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
+   the file LICENSE for additional details.
 
  */
 
 
       dcr->dev->num_parts = dcr->VolCatInfo.VolCatParts;
    }
 
-    Dmsg2(300, "do_reqest_vol_info got slot=%d Volume=%s\n",
+    Dmsg2(300, "do_reqest_vol_info return true slot=%d Volume=%s\n",
           vol.Slot, vol.VolCatName);
     return true;
 }
     bnet_fsend(dir, Get_Vol_Info, jcr->Job, dcr->VolCatInfo.VolCatName,
        writing==GET_VOL_INFO_FOR_WRITE?1:0);
     Dmsg1(100, ">dird: %s", dir->msg);
-    return do_get_volume_info(dcr);
+    bool OK = do_get_volume_info(dcr);
+    return OK;
 }
 
 /*
        unbash_spaces(dcr->media_type);
        unbash_spaces(dcr->pool_name);
        Dmsg1(100, ">dird: %s", dir->msg);
-       if (do_get_volume_info(dcr)) {
-          if (is_volume_in_use(dcr->VolumeName)) {
+       bool OK = do_get_volume_info(dcr);
+       if (OK) {
+          if (is_volume_in_use(dcr)) {
+             Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
              continue;
           } else {
              found = true;
       vol->VolCatParts);
     Dmsg1(100, ">dird: %s", dir->msg);
 
+   /* Do not lock device here because it may be locked from label */
    if (!do_get_volume_info(dcr)) {
       Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
       Pmsg2(000, "Didn't get vol info vol=%s: ERR=%s", 
    bool first = true;
    DEVICE *dev = dcr->dev;
    JCR *jcr = dcr->jcr;
+   bool OK = false;
 
    Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
    ASSERT(dev->dev_blocked);
          return false;
       }
       /* First pass, we *know* there are no appendable volumes, so no need to call */
-      if (!first && dir_find_next_appendable_volume(dcr)) { /* get suggested volume */
+      if (!first) {
+         P(dev->mutex);
+         OK = dir_find_next_appendable_volume(dcr);   /* get suggested volume */
+         V(dev->mutex);
+      }
+      if (!first && OK) {
          unmounted = is_device_unmounted(dev);
          /*
           * If we have a valid volume name and we are not
       Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
 
       /* If no VolumeName, and cannot get one, try again */
+      P(dev->mutex);
       if (dcr->VolumeName[0] == 0 && !job_canceled(jcr) &&
           !dir_find_next_appendable_volume(dcr)) {
+         V(dev->mutex);
          Jmsg(jcr, M_MOUNT, 0, _(
 "Someone woke me up, but I cannot find any appendable\n"
 "volumes for Job=%s.\n"), jcr->Job);
          init_device_wait_timers(dcr);
          continue;
       }
+      V(dev->mutex);
       unmounted = is_device_unmounted(dev);
       if (unmounted) {
+         Dmsg0(400, "Device is unmounted. Must wait.\n");
          continue;                    /* continue to wait */
       }
 
          Jmsg(jcr, M_MOUNT, 0, _("%s Volume \"%s\" on Storage Device %s for Job %s\n"),
               msg, dcr->VolumeName, dev->print_name(), jcr->Job);
          Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
-               dcr->VolumeName, dcr->dev_name, jcr->Job);
+               dcr->VolumeName, dev->print_name(), jcr->Job);
       }
 
       jcr->JobStatus = JS_WaitMount;
 
 
       case 'i':                    /* include list */
          if ((fd = fopen(optarg, "r")) == NULL) {
-            Pmsg2(0, "Could not open include file: %s, ERR=%s\n",
+            Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
                optarg, strerror(errno));
             exit(1);
          }
 
          return false;
       }
    }
-   Pmsg1(000, "open_ ev %s OK\n", dev->print_name());
+   Pmsg1(000, "open device %s: OK\n", dev->print_name());
    dev->set_append();                 /* put volume in append mode */
    unlock_device(dev);
    free_block(block);
 
    dev->no_wait_id = hold->no_wait_id;
    Dmsg1(400, "return lock. new=%s\n", dev->print_blocked());
    if (dev->num_waiting > 0) {
-      Dmsg0(400, "Broadcase\n");
+      Dmsg0(400, "Broadcast\n");
       pthread_cond_broadcast(&dev->wait); /* wake them up */
    }
 }
 
  *
  *    N.B. in this file, in general we must use P(dev->mutex) rather
  *      than lock_device(dev) so that we can examine the blocked
- *      state rather than blocking ourselves. In some "safe" cases,
+ *      state rather than blocking ourselves because a Job
+ *      thread has the device blocked. In some "safe" cases,
  *      we can do things to a blocked device. CAREFUL!!!!
  *
  *    File daemon commands are handled in fdcmd.c
 extern char my_name[];
 extern time_t daemon_start_time;
 extern struct s_last_job last_job;
+extern bool init_done;
 
 /* Static variables */
 static char derrmsg[]     = "3900 Invalid command\n";
          break;               /* connection terminated */
       }
       Dmsg1(199, "<dird: %s\n", bs->msg);
+      /* Ensure that device initialization is complete */
+      while (!init_done) {
+         bmicrosleep(1, 0);
+      }
       found = false;
       for (i=0; cmds[i].cmd; i++) {
         if (strncmp(cmds[i].cmd, bs->msg, strlen(cmds[i].cmd)) == 0) {
 
    const char *type;
    int dbl;
 
+   if (rec->FileIndex == 0 && rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
+      return;
+   }
    dbl = debug_level;
    debug_level = 1;
    switch (rec->FileIndex) {
          dump_session_label(rec, type);
          break;
       case EOM_LABEL:
-         Pmsg5(-1, "%s Record: SessId=%d SessTime=%d JobId=%d DataLen=%d\n",
-            type, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
+         Pmsg7(-1, "%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d DataLen=%d\n",
+            type, dev->file, dev->block_num, rec->VolSessionId, 
+            rec->VolSessionTime, rec->Stream, rec->data_len);
          break;
       case EOT_LABEL:
          Pmsg0(-1, _("End of physical tape.\n"));
          break;
       default:
-         Pmsg5(-1, "%s Record: SessId=%d SessTime=%d JobId=%d DataLen=%d\n",
-            type, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
+         Pmsg7(-1, "%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d DataLen=%d\n",
+            type, dev->file, dev->block_num, rec->VolSessionId, 
+            rec->VolSessionTime, rec->Stream, rec->data_len);
          break;
       }
    } else {
       switch (rec->FileIndex) {
       case SOS_LABEL:
          unser_session_label(&label, rec);
-         Pmsg6(-1, "%s Record: SessId=%d SessTime=%d JobId=%d Level=%c Type=%c\n",
-            type, rec->VolSessionId, rec->VolSessionTime, rec->Stream,
+         Pmsg8(-1, "%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d Level=%c Type=%c\n",
+            type, dev->file, dev->block_num, rec->VolSessionId, rec->VolSessionTime, rec->Stream,
             label.JobLevel, label.JobType);
          break;
       case EOS_LABEL:
          char ed1[30], ed2[30];
          unser_session_label(&label, rec);
-         Pmsg6(-1, "%s Record: SessId=%d SessTime=%d JobId=%d Level=%c Type=%c\n",
-            type, rec->VolSessionId, rec->VolSessionTime, rec->Stream,
+         Pmsg8(-1, "%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d Level=%c Type=%c\n",
+            type, dev->file, dev->block_num, rec->VolSessionId, rec->VolSessionTime, rec->Stream,
             label.JobLevel, label.JobType);
          Pmsg4(-1, "   Files=%s Bytes=%s Errors=%d Status=%c\n",
             edit_uint64_with_commas(label.JobFiles, ed1),
       case PRE_LABEL:
       case VOL_LABEL:
       default:
-         Pmsg5(-1, "%s Record: SessId=%d SessTime=%d JobId=%d DataLen=%d\n",
-      type, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
+         Pmsg7(-1, "%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d DataLen=%d\n",
+            type, dev->file, dev->block_num, rec->VolSessionId, rec->VolSessionTime, 
+            rec->Stream, rec->data_len);
          break;
       case EOT_LABEL:
          break;
 
             dcr->VolumeName, dev->print_name());
          goto read_volume;      /* read label we just wrote */
       }
+      if (!dev_cap(dev, CAP_LABEL) && dcr->VolCatInfo.VolCatBytes == 0) {
+         Jmsg(jcr, M_INFO, 0, _("Warning device %s not configured to autolabel Volumes.\n"), 
+            dev->print_name());
+      }
       /* If not removable, Volume is broken */
       if (!dev_cap(dev, CAP_REM)) {
          Jmsg(jcr, M_WARNING, 0, _("Volume \"%s\" not on device %s.\n"),
 
 void    create_volume_list();
 void    free_volume_list();
 void    list_volumes(BSOCK *user);
-bool    is_volume_in_use(const char *VolumeName);
+bool    is_volume_in_use(DCR *dcr);
 
 
 /* From spool.c */
 
 /* Create the Volume list */
 void create_volume_list()
 {
-   VOLRES *dummy;
+   VOLRES *dummy = NULL;
    if (vol_list == NULL) {
       vol_list = New(dlist(dummy, &dummy->link));
    }
    vol_list = NULL;
 }
 
-bool is_volume_in_use(const char *VolumeName) 
+bool is_volume_in_use(DCR *dcr)
 {
-   VOLRES *vol = find_volume(VolumeName);
+   VOLRES *vol = find_volume(dcr->VolumeName);
    if (!vol) {
       return false;                   /* vol not in list */
    }
    if (!vol->dev) {                   /* vol not attached to device */
       return false;
    }
+   if (dcr->dev == vol->dev) {        /* same device OK */
+      return false;
+   }
    if (!vol->dev->is_busy()) {
       return false;
    }
 
 static uint32_t VolSessionId = 0;
 uint32_t VolSessionTime;
 char *configfile = NULL;
+bool init_done = false;
 
 /* Global static variables */
 static int foreground = 0;
    /* Make sure on Solaris we can run concurrent, watch dog + servers + misc */
    set_thread_concurrency(me->max_concurrent_jobs * 2 + 4);
 
-   create_volume_list();
     /*
      * Start the device allocation thread
      */
       Emsg1(M_ABORT, 0, _("Unable to create thread. ERR=%s\n"), strerror(errno));
    }
 
+   create_volume_list();
    start_watchdog();                  /* start watchdog thread */
-
    init_jcr_subsystem();              /* start JCR watchdogs etc. */
 
-   /*
-    * Sleep a bit to give device thread a chance to lock the resource
-    * chain before we start the server.
-    */
-   bmicrosleep(1, 0);
-
-   /* Wait for device initialization to complete */
-   LockRes();
-   UnlockRes();
-
    /* Single server used for Director and File daemon */
    bnet_thread_server(me->sdaddrs, me->max_concurrent_jobs * 2 + 1,
                       &dird_workq, handle_connection_request);
       free_dcr(dcr);
    }
    free_jcr(jcr); 
+   init_done = true;
    UnlockRes();
    return NULL;
 }
 
 /* */
 #undef  VERSION
-#define VERSION "1.37.28"
-#define BDATE   "09 July 2005"
-#define LSMDATE "09Jul05"
+#define VERSION "1.37.29"
+#define BDATE   "11 July 2005"
+#define LSMDATE "11Jul05"
 
 /* Debug flags */
 #undef  DEBUG
 /* The following are turned on for performance testing */
 /* #define NO_ATTRIBUTES_TEST 1 */
 /* #define NO_TAPE_WRITE_TEST 1 */
-/* #define FD_NO_SEND TEST 1 */
+/* #define FD_NO_SEND_TEST 1 */