]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
BEE Backport bacula/src/stored/dircmd.c
authorEric Bollengier <eric@baculasystems.com>
Tue, 12 May 2020 16:50:04 +0000 (18:50 +0200)
committerEric Bollengier <eric@baculasystems.com>
Thu, 29 Apr 2021 08:44:18 +0000 (10:44 +0200)
This commit is the result of the squash of the following main commits:

Author: Marcin Haba <marcin.haba@bacula.pl>
Date:   Thu Mar 19 10:05:32 2020 +0100

    Fix #6085 voltype setting when tape volume is labeled

Author: Eric Bollengier <eric@baculasystems.com>
Date:   Fri Feb 21 11:05:36 2020 +0100

    Fix #5999 about Director connection in status storage output

Author: Eric Bollengier <eric@baculasystems.com>
Date:   Tue Dec 3 11:10:47 2019 +0100

    Move new_match_all() to BEE module

Author: Eric Bollengier <eric@baculasystems.com>
Date:   Mon Dec 2 16:09:07 2019 +0100

    Fix compilation with COMMUNITY

Author: Norbert Bizet <norbert.bizet@baculasystems.com>
Date:   Wed Nov 6 12:13:46 2019 -0500

    cloud: Fix #5537 if dev errmsg exists, display it otherwise use jcr errmsg

Author: Norbert Bizet <norbert.bizet@baculasystems.com>
Date:   Wed Nov 6 10:32:29 2019 -0500

    cloud: Enhance cache truncate command output

Author: Norbert Bizet <norbert.bizet@baculasystems.com>
Date:   Wed Nov 6 09:28:39 2019 -0500

    cloud: Fix #5536 make sure truncate_cache as cloud proxy up-to-date

Author: Norbert Bizet <norbert.bizet@baculasystems.com>
Date:   Mon Apr 29 10:07:01 2019 -0400

    cloud: compute part hash at upload and download

    Use GENERATE_CLOUD_HASH to enable it.
    Not enabled with S3 cloud.

Author: Alain Spineux <alain@baculasystems.com>
Date:   Fri Apr 12 04:55:10 2019 +0200

    PSK: encrypt copy jobs (SD-SD )

Author: Alain Spineux <alain@baculasystems.com>
Date:   Mon Apr 8 14:56:45 2019 +0200

    PSK: Modify authentication in each daemon to support PSK

    - use AuthenticateBase class

Author: Eric Bollengier <eric@baculasystems.com>
Date:   Thu Mar 21 10:21:21 2019 +0100

    Ensure that a job message is printed after some unespected close() errors

Author: Alain Spineux <alain@baculasystems.com>
Date:   Tue Oct 31 16:11:41 2017 +0100

    add a jcr->set_killable(true) to the SD and FD

    - under a "#ifdef DEVELOPER" to start

Author: Eric Bollengier <eric@baculasystems.com>
Date:   Mon Aug 28 17:01:40 2017 +0200

    Write the list of selected files to a temporary file if requested by a Plugin

    If we find a RestoreObject with the type FT_PLUGIN_FILELIST, we list all
    selected files in a temporary file to send it to the FD before the actual
    restore as a RestoreObject.

Author: Norbert Bizet <norbert.bizet@baculasystems.com>
Date:   Mon May 22 16:36:40 2017 +0200

    cloud: Add "Manual" cloud upload option and add truncate option to the upload command.

Author: Kern Sibbald <kern@sibbald.com>
Date:   Sun May 21 17:22:58 2017 +0200

    Fix race in steal_device_lock shown in truncate-concurrent-test

Author: Kern Sibbald <kern@sibbald.com>
Date:   Fri Oct 21 13:27:07 2016 +0200

    Implement truncate cache command

Author: Kern Sibbald <kern@sibbald.com>
Date:   Fri Dec 11 09:51:53 2015 +0100

    Fix truncate race bug #1382

Author: Kern Sibbald <kern@sibbald.com>
Date:   Mon Feb 24 17:16:21 2014 +0100

    Implement sd_calls_client for Migrate/Copy jobs

Author: Kern Sibbald <kern@sibbald.com>
Date:   Sat Feb 1 23:16:04 2014 +0100

    Implement SD-SD migrate/copy code

Author: Kern Sibbald <kern@sibbald.com>
Date:   Sat Jan 4 17:30:07 2014 +0100

    Implement SD Calls Client

    which is a prerequesite for SD->SD

Author: Kern Sibbald <kern@sibbald.com>
Date:   Tue May 8 18:51:01 2012 +0200

    Always set autochanger even for Storage devices + ensure shared storage unlocked on error exit

Author: Kern Sibbald <kern@sibbald.com>
Date:   Sat Jan 28 20:49:50 2012 +0100

    Fix (hopefully) shstore locking in dircmd.c of SD

bacula/src/stored/dircmd.c

index d0d23773614b52078f96066666c045dc44c0abd1..c86ff7330f3c1e9c9011fd864765184639e8de82 100644 (file)
@@ -18,7 +18,7 @@
 */
 /*
  *  This file handles accepting Director Commands
- *
+ * 
  *    Most Director commands are handled here, with the
  *    exception of the Job command command and subsequent
  *    subcommands that are handled
@@ -96,6 +96,9 @@ static void label_volume_if_ok(DCR *dcr, char *oldname,
                                int Slot, int relabel);
 static bool try_autoload_device(JCR *jcr, DCR *dcr, int slot, const char *VolName);
 static void send_dir_busy_message(BSOCK *dir, DEVICE *dev);
+bool user_shstore_lock(DCR *dcr);
+void user_shstore_unlock(DCR *dcr);
+bool dedup_cmd(JCR *jcr); /* BEEF */
 
 /* Responses send to Director for storage command */
 static char BADcmd[]  = "2902 Bad %s\n";
@@ -138,6 +141,9 @@ static struct s_cmds cmds[] = {
    {"cloudlist",   cloud_list_cmd,  0},     /* List volumes/parts in the cloud */
    {"unmount",     unmount_cmd,     0},
    {"use storage=", use_cmd,        0},
+#if BEEF
+   {"dedup",       dedup_cmd,       0},     /* any dedup command */
+#endif
    {"run",         run_cmd,         0},
    {"statistics",  collect_cmd,     0},
 // {"query",       query_cmd,       0},
@@ -189,6 +195,8 @@ void *handle_connection_request(void *arg)
    Dmsg1(050, "Got a DIR connection at %s\n", bstrftimes(tbuf, sizeof(tbuf),
          (utime_t)time(NULL)));
    jcr = new_jcr(sizeof(JCR), stored_free_jcr); /* create Job Control Record */
+   jcr->set_killable(true);
+   jcr->start_time = time(NULL);
    jcr->dir_bsock = bs;               /* save Director bsock */
    jcr->dir_bsock->set_jcr(jcr);
    jcr->dcrs = New(alist(10, not_owned_by_alist));
@@ -209,16 +217,15 @@ void *handle_connection_request(void *arg)
    if (!validate_dir_hello(jcr)) {
       goto bail_out;
    }
-   if (!authenticate_director(jcr)) {
-      Qmsg(jcr, M_FATAL, 0, _("[SF0100] Unable to authenticate Director\n"));
-      goto bail_out;
+   {
+      SDAuthenticateDIR auth(jcr);
+      if (!auth.authenticate_director()) {
+         Qmsg(jcr, M_FATAL, 0, _("[SF0100] Unable to authenticate Director\n"));
+         goto bail_out;
+      }
    }
    Dmsg0(90, "Message channel init completed.\n");
 
-   dequeue_messages(jcr);     /* dequeue any daemon messages */
-
-   jcr->set_killable(true);    /* allow dir to kill/cancel job */
-
    for (quit=false; !quit;) {
       /* Read command */
       if ((bnet_stat = bs->recv()) <= 0) {
@@ -261,8 +268,6 @@ bail_out:
    dequeue_messages(jcr);             /* send any queued messages */
    dequeue_daemon_messages(jcr);
    bs->signal(BNET_TERMINATE);
-   bs->destroy();
-   jcr->dir_bsock = NULL;             /* just freed bsock */
    free_plugins(jcr);                 /* release instantiated plugins */
    free_jcr(jcr);
    return NULL;
@@ -311,8 +316,10 @@ static bool client_cmd(JCR *jcr)
    jcr->sd_calls_client = true;
    if (sscanf(dir->msg, "client address=%127s port=%d ssl=%d", jcr->client_addr, &client_port,
               &enable_ssl) != 3) {
+      /* destroy() OK because cl is local */
+      cl->destroy();
       pm_strcpy(jcr->errmsg, dir->msg);
-      Jmsg(jcr, M_FATAL, 0, _("[SF0101] Bad client command: %s"), jcr->errmsg);
+      Jmsg(jcr, M_FATAL, 0, _("Bad client command: %s"), jcr->errmsg);
       Dmsg1(050, "Bad client command: %s", jcr->errmsg);
       goto bail_out;
    }
@@ -335,6 +342,8 @@ static bool client_cmd(JCR *jcr)
 
    jcr->file_bsock = cl;
    jcr->file_bsock->set_jcr(jcr);
+
+   // This is a "dummy" hello, the FD will send the "real" hello with a TLS-PSK info (or not)
    if (!send_hello_client(jcr, jcr->Job)) {
       goto bail_out;
    }
@@ -409,13 +418,10 @@ static bool storage_cmd(JCR *jcr)
       }
    }
 
-   if (!send_hello_sd(jcr, Job)) {
+   if (!send_hello_and_authenticate_sd(jcr, Job)) {
       goto bail_out;
    }
 
-   if (!authenticate_storagedaemon(jcr)) {
-      goto bail_out;
-   }
    /*
     * We are a client so we read from the socket we just
     *   opened as if we were a FD, so set file_bsock and
@@ -472,18 +478,7 @@ static bool setdebug_cmd(JCR *jcr)
    }
    debug_level_tags = level_tags;
 
-   /* TODO: Temp code to activate the new BSR optimisation code */
-   for (char *p = options; *p ; p++) {
-      switch(*p) {
-      case 'i':                 /* Use new match_bsr() code */
-         use_new_match_all = 1;
-         break;
-      case '0':
-         use_new_match_all = 0;
-         break;
-      }
-   }
-   /* **** */
+   bee_setdebug_cmd_parse_options(jcr, options);
 
    return dir->fsend(OKsetdebug, lvl, trace_flag, options, tags);
 }
@@ -614,12 +609,12 @@ static bool do_label(JCR *jcr, int relabel)
             if (reserve_volume(dcr, newname) == NULL) {
                ok = false;
             }
-            Dmsg1(400, "Reserved Volume=\"%s\" for relabel/truncate.\n", newname);
+            Dmsg1(400, "Reserved Volume=%s for relabel/truncate.\n", newname);
          } else {
             ok = false;
          }
          if (!ok) {
-            dir->fsend(_("3908 Error reserving Volume=\"%s\": %s"), newname, jcr->errmsg);
+            dir->fsend(_("3908 Error reserving volume: %s\n"), jcr->errmsg);
             dev->max_concurrent_jobs = max_jobs;
             dev->Unlock();
             goto bail_out;
@@ -632,7 +627,9 @@ static bool do_label(JCR *jcr, int relabel)
          if (!dev->is_open() && !dev->is_busy()) {
             Dmsg1(400, "Can %slabel. Device is not open\n", relabel?"re":"");
             label_volume_if_ok(dcr, oldname, newname, poolname, slot, relabel);
-            dev->close(dcr);
+            if (!dev->close(dcr)) {
+               dir->fsend(_("3999 Device \"%s\" %s"), dev_name.c_str(), dev->errmsg);
+            }
          /* Under certain "safe" conditions, we can steal the lock */
          } else if (dev->can_obtain_block()) {
             Dmsg0(400, "Can relabel. can_obtain_block\n");
@@ -678,10 +675,10 @@ bail_out:
  */
 static bool truncate_cache_cmd(JCR *jcr)
 {
-   POOLMEM *volname, *poolname, *mtype;
+   POOLMEM *volname, *poolname, *mtype, *msg;
    POOL_MEM dev_name;
    BSOCK *dir = jcr->dir_bsock;
-   DCR *dcr = NULL;;
+   DCR *dcr = NULL;
    DEVICE *dev;
    bool ok = false;
    int32_t slot, drive;
@@ -692,6 +689,8 @@ static bool truncate_cache_cmd(JCR *jcr)
    volname = get_memory(dir->msglen+1);
    poolname = get_memory(dir->msglen+1);
    mtype = get_memory(dir->msglen+1);
+   msg = get_memory(dir->msglen+1);
+   msg[0] = 0;
    if (sscanf(dir->msg, "truncate cache Storage=%127s Volume=%127s PoolName=%127s "
               "MediaType=%127s Slot=%d drive=%d",
                dev_name.c_str(), volname, poolname, mtype,
@@ -720,17 +719,17 @@ static bool truncate_cache_cmd(JCR *jcr)
             ok = false;
          }
          if (!ok) {
-            dir->fsend(_("3908 Error reserving Volume=\"%s\": %s"), volname, jcr->errmsg);
+            dir->fsend(_("3908 Error reserving volume \"%s\": %s\n"), volname, jcr->errmsg);
             dev->max_concurrent_jobs = max_jobs;
             dev->Unlock();
             goto bail_out;
          }
          if ((!dev->is_open() && !dev->is_busy()) || dev->can_obtain_block()) {
             Dmsg0(400, "Call truncate_cache\n");
-            nbpart = dev->truncate_cache(dcr, volname, &size);
+            nbpart = dev->truncate_cache(dcr, volname, &size, msg);
             if (nbpart >= 0) {
-               dir->fsend(_("3000 OK truncate cache for volume \"%s\" %d part(s) %sB\n"),
-                          volname, nbpart, edit_uint64_with_suffix(size, ed1));
+               dir->fsend(_("3000 OK truncate cache for volume \"%s\" %d part(s) %sB. %s\n"),
+                          volname, nbpart, edit_uint64_with_suffix(size, ed1), msg);
             } else {
                dir->fsend(_("3900 Truncate cache for volume \"%s\" failed. ERR=%s\n"), volname, dev->errmsg);
             }
@@ -738,14 +737,15 @@ static bool truncate_cache_cmd(JCR *jcr)
             send_dir_busy_message(dir, dev);
          } else {                     /* device not being used */
             Dmsg0(400, "Call truncate_cache\n");
-            nbpart = dev->truncate_cache(dcr, volname, &size);
+            nbpart = dev->truncate_cache(dcr, volname, &size, msg);
             if (nbpart >= 0) {
-               dir->fsend(_("3000 OK truncate cache for volume \"%s\" %d part(s) %sB\n"), volname,
-                          nbpart, edit_uint64_with_suffix(size, ed1));
+               dir->fsend(_("3000 OK truncate cache for volume \"%s\" %d part(s) %sB. %s\n"), volname,
+                          nbpart, edit_uint64_with_suffix(size, ed1), msg);
             } else {
                dir->fsend(_("3900 Truncate cache for volume \"%s\" failed. ERR=%s\n"), volname, dev->errmsg);
             }
          }
+
          dev->max_concurrent_jobs = max_jobs;
          volume_unused(dcr);
          dev->Unlock();
@@ -768,6 +768,7 @@ bail_out:
    if (dcr) {
       free_dcr(dcr);
    }
+   free_memory(msg);
    free_memory(volname);
    free_memory(poolname);
    free_memory(mtype);
@@ -835,7 +836,11 @@ static bool cloud_list_cmd(JCR *jcr)
       for (int i=1; i <= parts.last_index() ; i++) {
          cloud_part *p = (cloud_part *)parts.get(i);
          if (p) {
-            dir->fsend("part=%d size=%lld mtime=%lld\n", i, p->size, p->mtime);
+            if (p->hash64) {
+               dir->fsend("part=%d size=%lld mtime=%lld hash=%s\n", i, p->size, p->mtime, p->hash64);
+            } else { 
+               dir->fsend("part=%d size=%lld mtime=%lld hash=%s\n", i, p->size, p->mtime);
+            }
             free(p);
          }
       }
@@ -852,7 +857,7 @@ bail_out:
  */
 static bool upload_cmd(JCR *jcr)
 {
-   POOLMEM *volname, *poolname, *mtype, *err;
+   POOLMEM *volname, *poolname, *mtype, *truncate, *err;
    POOL_MEM dev_name;
    BSOCK *dir = jcr->dir_bsock;
    DCR *dcr = NULL;
@@ -863,19 +868,21 @@ static bool upload_cmd(JCR *jcr)
    volname = get_memory(dir->msglen+1);
    poolname = get_memory(dir->msglen+1);
    mtype = get_memory(dir->msglen+1);
+   truncate = get_memory(dir->msglen+1);
    err = get_pool_memory(PM_MESSAGE);
-   *volname = *poolname = *mtype = *err = 0;
+   *volname = *poolname = *mtype = *truncate = *err = 0;
 
    if (sscanf(dir->msg, "upload Storage=%127s Volume=%127s PoolName=%127s "
-              "MediaType=%127s Slot=%d drive=%d",
+              "MediaType=%127s Slot=%d drive=%d truncate=%127s",
                dev_name.c_str(), volname, poolname, mtype,
-               &slot, &drive) == 6) {
+               &slot, &drive, truncate) == 7) {
       ok = true;
    }
    if (ok) {
       unbash_spaces(volname);
       unbash_spaces(poolname);
       unbash_spaces(mtype);
+      unbash_spaces(truncate);
       dcr = find_device(jcr, dev_name, mtype, drive);
       if (dcr) {
          uint32_t max_jobs;
@@ -894,20 +901,28 @@ static bool upload_cmd(JCR *jcr)
             ok = false;
          }
          if (!ok) {
-            dir->fsend(_("3908 Error reserving Volume=\"%s\": %s"), volname, jcr->errmsg);
+            dir->fsend(_("3908 Error reserving volume \"%s\": %s\n"), volname, jcr->errmsg);
             dev->max_concurrent_jobs = max_jobs;
             dev->Unlock();
             goto bail_out;
          }
+         uint32_t truncate_option = 0;
+         /* modify truncate_option if truncate matches a truncate option */
+         if (!find_truncate_option(truncate, truncate_option)) {
+            Dmsg1(400, "truncate option %s does not exist. Fallback on default configuration truncate option\n", truncate);
+            truncate_option = TRUNC_CONF_DEFAULT;
+         }
          if ((!dev->is_open() && !dev->is_busy()) || dev->can_obtain_block()) {
             Dmsg0(400, "Can upload, because device is not open.\n");
             dev->setVolCatName(volname);
             dev->part = 0;
             if (dev->open_device(dcr, OPEN_READ_WRITE)) {
-               ok = dev->upload_cache(dcr, volname, err);
+               ok = dev->upload_cache(dcr, volname, truncate_option, err);
                dev->part = 0;
-               dev->close(dcr);
-               dev->end_of_job(dcr);
+               if (!dev->close(dcr)) {
+                  dir->fsend(_("3999 Error Device \"%s\": %s\n"), dev_name.c_str(), dev->errmsg);
+               }
+               dev->end_of_job(dcr, truncate_option);
             }
          } else if (dev->is_busy() || dev->is_blocked()) {
             send_dir_busy_message(dir, dev);
@@ -916,10 +931,12 @@ static bool upload_cmd(JCR *jcr)
             dev->setVolCatName(volname);
             dev->part = 0;
             if (dev->open_device(dcr, OPEN_READ_WRITE)) {
-               ok = dev->upload_cache(dcr, volname, err);
+               ok = dev->upload_cache(dcr, volname, truncate_option, err);
                dev->part = 0;
-               dev->close(dcr);
-               dev->end_of_job(dcr);
+               if (!dev->close(dcr)) {
+                  dir->fsend(_("3999 Error Device \"%s\": %s\n"), dev_name.c_str(), dev->errmsg);
+               }
+               dev->end_of_job(dcr, truncate_option);
             }
          }
          dev->max_concurrent_jobs = max_jobs;
@@ -953,6 +970,7 @@ bail_out:
    free_memory(volname);
    free_memory(poolname);
    free_memory(mtype);
+   free_memory(truncate);
    dir->signal(BNET_EOD);
    return true;
 }
@@ -987,6 +1005,10 @@ static void label_volume_if_ok(DCR *dcr, char *oldname,
 
    Dmsg1(100, "Stole device %s lock, writing label.\n", dev->print_name());
 
+   if (!user_shstore_lock(dcr)) {
+      goto bail_out;
+   }
+
    Dmsg0(90, "try_autoload_device - looking for volume_info\n");
    if (!try_autoload_device(dcr->jcr, dcr, slot, volname)) {
       goto bail_out;                  /* error */
@@ -1042,8 +1064,13 @@ static void label_volume_if_ok(DCR *dcr, char *oldname,
    case VOL_NO_LABEL:
       if (!dev->write_volume_label(dcr, newname, poolname,
               relabel, true /* write label now */)) {
-         dir->fsend(_("3912 Failed to label Volume %s: ERR=%s\n"),
-            newname, dcr->jcr->errmsg);
+         if (dev->errmsg[0]) {
+            dir->fsend(_("3912 Failed to label Volume %s: ERR=%s\n"),
+               newname, dev->errmsg);
+         } else {
+            dir->fsend(_("3912 Failed to label Volume %s: ERR=%s\n"),
+               newname, dcr->jcr->errmsg);
+         }
          break;
       }
       volCatBytes = dev->VolCatInfo.VolCatBytes;
@@ -1061,7 +1088,8 @@ static void label_volume_if_ok(DCR *dcr, char *oldname,
       /* The following 3000 OK label. string is scanned in ua_label.c */
       int type;
       if (dev->dev_type == B_FILE_DEV || dev->dev_type == B_ALIGNED_DEV ||
-          dev->dev_type == B_CLOUD_DEV)
+          dev->dev_type == B_CLOUD_DEV || dev->dev_type == B_DEDUP_DEV ||
+          dev->dev_type == B_TAPE_DEV)
       {
          type = dev->dev_type;
       } else {
@@ -1085,10 +1113,13 @@ static void label_volume_if_ok(DCR *dcr, char *oldname,
 
 bail_out:
    if (dev->is_open() && !dev->has_cap(CAP_ALWAYSOPEN)) {
-      dev->close(dcr);
+      if (!dev->close(dcr)) {
+         dir->fsend(_("3999 Error Device \"%s\": %s\n"),
+                    dev->print_name(), dev->errmsg);
+      }
    }
 
-   dev->end_of_job(dcr);
+   dev->end_of_job(dcr, TRUNC_CONF_DEFAULT);
 
    if (!dev->is_open()) {
       dev->clear_volhdr();
@@ -1096,6 +1127,7 @@ bail_out:
    volume_unused(dcr);                   /* no longer using volume */
    dev->Lock();
    give_back_device_block(dev, &hold);
+   user_shstore_unlock(dcr);
    return;
 }
 
@@ -1331,6 +1363,9 @@ static bool mount_cmd(JCR *jcr)
          case BST_UNMOUNTED_WAITING_FOR_SYSOP:
          case BST_UNMOUNTED:
             Dmsg2(100, "Unmounted changer=%d slot=%d\n", dev->is_autochanger(), slot);
+            if (!user_shstore_lock(dcr)) {
+               break;
+            }
             if (dev->is_autochanger() && slot > 0) {
                try_autoload_device(jcr, dcr, slot, "");
             }
@@ -1343,6 +1378,7 @@ static bool mount_cmd(JCR *jcr)
                   Dmsg0(100, "Unmounted. Unblocking device\n");
                   unblock_device(dev);
                }
+               user_shstore_unlock(dcr);
                break;
             }
             dev->read_dev_volume_label(dcr);
@@ -1363,6 +1399,7 @@ static bool mount_cmd(JCR *jcr)
                                  "If this is not a blank tape, try unmounting and remounting the Volume.\n"),
                           dev->print_name());
             }
+            user_shstore_unlock(dcr);
             pthread_cond_broadcast(&dev->wait_next_vol);
             Dmsg1(100, "JobId=%u broadcast wait_device_release\n", (uint32_t)dcr->jcr->JobId);
             pthread_cond_broadcast(&wait_device_release);
@@ -1393,9 +1430,13 @@ static bool mount_cmd(JCR *jcr)
                              dev->print_name());
                }
             } else if (dev->is_tape()) {
+               if (!user_shstore_lock(dcr)) {
+                  break;
+               }
                if (!dev->open_device(dcr, OPEN_READ_ONLY)) {
                   dir->fsend(_("3901 Unable to open device \"%s\": ERR=%s\n"),
                      dev->print_name(), dev->bstrerror());
+                  user_shstore_unlock(dcr);
                   break;
                }
                read_label(dcr);
@@ -1408,8 +1449,11 @@ static bool mount_cmd(JCR *jcr)
                              dev->print_name());
                }
                if (dev->is_open() && !dev->has_cap(CAP_ALWAYSOPEN)) {
-                  dev->close(dcr);
+                  if (!dev->close(dcr)) {
+                     dir->fsend(_("3999 Error Device \"%s\": %s\n"), dev->print_name(), dev->errmsg);
+                  }
                }
+               user_shstore_unlock(dcr);
             } else if (dev->is_unmountable()) {
                if (dev->mount(1)) {
                   dir->fsend(_("3002 Device \"%s\" is mounted.\n"), dev->print_name());
@@ -1469,7 +1513,7 @@ static bool enable_cmd(JCR *jcr)
             dir->fsend(_("3003 Device \"%s\" already enabled.\n"), dev->print_name());
          } else {
             dev->enabled = true;
-            dev->devstatcollector->set_value_bool(dev->devstatmetrics.bacula_storage_device_status, true);
+           dev->devstatcollector->set_value_bool(dev->devstatmetrics.bacula_storage_device_status, true);
             dir->fsend(_("3002 Device \"%s\" enabled.\n"), dev->print_name());
          }
          deleted = dev->delete_alerts();
@@ -1508,7 +1552,7 @@ static bool disable_cmd(JCR *jcr)
          dev = dcr->dev;
          dev->Lock();
          dev->enabled = false;
-         dev->devstatcollector->set_value_bool(dev->devstatmetrics.bacula_storage_device_status, false);
+        dev->devstatcollector->set_value_bool(dev->devstatmetrics.bacula_storage_device_status, false);
          dir->fsend(_("3002 Device \"%s\" disabled.\n"), dev->print_name());
          dev->Unlock();
          free_dcr(dcr);
@@ -1563,7 +1607,9 @@ static bool unmount_cmd(JCR *jcr)
                 * ***FIXME**** what is this ???? -- probably we had
                 *   the wrong volume so we must free it and try again. KES
                 */
-               dev->close(dcr);
+               if (!dev->close(dcr)) {
+                  dir->fsend(_("3999 Error Device \"%s\": %s\n"), dev->print_name(), dev->errmsg);
+               }
                free_volume(dev);
             }
             if (dev->is_unmountable() && !dev->unmount(0)) {
@@ -1595,7 +1641,9 @@ static bool unmount_cmd(JCR *jcr)
             dev->set_blocked(BST_UNMOUNTED);
             clear_thread_id(dev->no_wait_id);
             if (!unload_autochanger(dcr, -1)) {
-               dev->close(dcr);
+               if (!dev->close(dcr)) {
+                  dir->fsend(_("3999 Error Device \"%s\": %s\n"), dev->print_name(), dev->errmsg);
+               }
                free_volume(dev);
             }
             if (dev->is_unmountable() && !dev->unmount(0)) {
@@ -1741,7 +1789,7 @@ static bool get_bootstrap_file(JCR *jcr, BSOCK *sock)
 
    if (jcr->RestoreBootstrap) {
       unlink(jcr->RestoreBootstrap);
-      free_pool_memory(jcr->RestoreBootstrap);
+      bfree_and_null(jcr->RestoreBootstrap);
    }
    P(bsr_mutex);
    bsr_uniq++;
@@ -1762,11 +1810,15 @@ static bool get_bootstrap_file(JCR *jcr, BSOCK *sock)
        Dmsg1(150, "%s", sock->msg);
        fputs(sock->msg, bs);
    }
-   fclose(bs);
+   if (fclose(bs) != 0) {
+      berrno be;
+      Jmsg(jcr, M_FATAL, 0, _("Error closing boostrap file. %s\n"), be.bstrerror());
+      goto bail_out;
+   }
    Dmsg0(150, "=== end bootstrap file ===\n");
    jcr->bsr = parse_bsr(jcr, jcr->RestoreBootstrap);
    if (!jcr->bsr) {
-      Jmsg(jcr, M_FATAL, 0, _("[SF0111] Error parsing bootstrap file.\n"));
+      Jmsg(jcr, M_FATAL, 0, _("Error parsing bootstrap file.\n"));
       goto bail_out;
    }
    if (chk_dbglvl(150)) {
@@ -1779,8 +1831,8 @@ static bool get_bootstrap_file(JCR *jcr, BSOCK *sock)
 
 bail_out:
    unlink(jcr->RestoreBootstrap);
-   free_pool_memory(jcr->RestoreBootstrap);
    jcr->RestoreBootstrap = NULL;
+   free_pool_memory(fname);
    if (!ok) {
       sock->fsend(ERROR_bootstrap);
       return false;
@@ -1872,8 +1924,9 @@ static bool readlabel_cmd(JCR *jcr)
          dev->Lock();                 /* Use P to avoid indefinite block */
          if (!dev->is_open()) {
             read_volume_label(jcr, dcr, dev, Slot);
-            dev->close(dcr);
-         /* Under certain "safe" conditions, we can steal the lock */
+            if (!dev->close(dcr)) {
+               dir->fsend(_("3999 Error Device \"%s\": %s\n"), dev->print_name(), dev->errmsg);
+            }         /* Under certain "safe" conditions, we can steal the lock */
          } else if (dev->can_obtain_block()) {
             read_volume_label(jcr, dcr, dev, Slot);
          } else if (dev->is_busy() || dev->is_blocked()) {
@@ -1988,3 +2041,11 @@ static void send_dir_busy_message(BSOCK *dir, DEVICE *dev)
           dev->print_name(), dev->num_writers, dev->num_reserved());
    }
 }
+
+#if COMMUNITY
+
+bool user_shstore_lock(DCR *dcr) { return true; }
+void user_shstore_unlock(DCR *dcr) {}
+bool dedup_cmd(JCR *jcr) { return true; }
+
+#endif