]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/filed/verify_vol.c
Big backport from Enterprise
[bacula/bacula] / bacula / src / filed / verify_vol.c
index aba6650500078dd8d102820a3e6af5795e388f96..5fba40c38b86fcdab0e4945d8ce37e62afdba1a3 100644 (file)
@@ -1,17 +1,20 @@
 /*
-   Bacula® - The Network Backup Solution
+   Bacula(R) - The Network Backup Solution
 
-   Copyright (C) 2002-2014 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2017 Kern Sibbald
 
-   The main author of Bacula is Kern Sibbald, with contributions from many
-   others, a complete list can be found in the file AUTHORS.
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
 
    You may use this file and others of this release according to the
    license defined in the LICENSE file, which includes the Affero General
    Public License, v3.0 ("AGPLv3") and some additional permissions and
    terms pursuant to its AGPLv3 Section 7.
 
-   Bacula® is a registered trademark of Kern Sibbald.
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
 */
 /*
  *  Bacula File Daemon  verify-vol.c Verify files on a Volume
  *
  *    Kern Sibbald, July MMII
  *
+ *  Data verification added by Eric Bollengier
  */
 
 #include "bacula.h"
 #include "filed.h"
+#include "findlib/win32filter.h"
+
+#if   defined(HAVE_LIBZ)
+const bool have_libz = true;
+#else
+const bool have_libz = false;
+#endif
+
+#ifdef HAVE_LZO
+const bool have_lzo = true;
+#else
+const bool have_lzo = false;
+#endif
+
+/* Context used during Verify Data job. We use it in the
+ * verify loop to compute checksums and check attributes.
+ */
+class v_ctx {
+public:
+   JCR *jcr;
+   int32_t stream;              /* stream less new bits */
+   int32_t prev_stream;         /* previous stream */
+   int32_t full_stream;         /* full stream including new bits */
+   int32_t type;                /* file type FT_ */
+   int64_t size;                /* current file size */
+   ATTR *attr;                  /* Pointer to attributes */
+
+   bool check_size;             /* Check or not the size attribute */
+   bool check_chksum;           /* Check the checksum */
+
+   crypto_digest_t digesttype;
+   Win32Filter win32filter;
+   char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)]; /* current digest */
+
+   v_ctx(JCR *ajcr) :
+      jcr(ajcr), stream(0), prev_stream(0), full_stream(0), type(0), size(-1),
+      attr(new_attr(jcr)), check_size(false), check_chksum(false),
+      digesttype(CRYPTO_DIGEST_NONE), win32filter()
+   {
+      *digest = 0;
+      scan_fileset();
+   };
+   ~v_ctx() {
+      free_attr(attr);
+   };
+   /* Call this function when we change the file
+    * We check the st_size and we compute the digest
+    */
+   bool close_previous_stream();
+
+   /* Call when we have a sparse record */
+   void skip_sparse_header(char **data, uint32_t *length);
+
+   /* Scan the fileset to know if we want to check checksums or st_size */
+   void scan_fileset();
+
+   /* Check the catalog to locate the file */
+   void check_accurate();
+
+   /* In cleanup, we reset the current file size to -1 */
+   void reset_size() {
+      size = -1;
+   };
+
+   /* Used for sparse files */
+   void set_size(int64_t val) {
+      size = MAX(size, val);
+   };
+
+   void update_size(int64_t val) {
+      if (size == -1) {
+         size = 0;
+      }
+      size += val;
+   };
+
+   void update_checksum(char *wbuf, int32_t wsize) {
+      if (wsize > 0 && check_chksum) {
+         if (!jcr->crypto.digest) {
+            jcr->crypto.digest = crypto_digest_new(jcr, digesttype);
+         }
+         crypto_digest_update(jcr->crypto.digest, (uint8_t *)wbuf, wsize);
+      }
+   };
+};
 
 /* Data received from Storage Daemon */
 static char rec_header[] = "rechdr %ld %ld %ld %ld %ld";
 
 /* Forward referenced functions */
 
+/* We don't know in advance which digest mode is needed, we do not
+ * want to store files on disk either to check afterward. So, we read
+ * the fileset definition and we try to guess the digest that will be
+ * used. If the FileSet uses multiple digests, it will not work.
+ */
+void v_ctx::scan_fileset()
+{
+   findFILESET *fileset;
+
+   check_size = check_chksum = false;
+   digesttype = CRYPTO_DIGEST_NONE;
+
+   if (!jcr->ff || !jcr->ff->fileset) {
+      return;
+   }
+
+   fileset = jcr->ff->fileset;
+
+   for (int i=0; i<fileset->include_list.size(); i++) {
+      findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
+
+      for (int j=0; j<incexe->opts_list.size(); j++) {
+         findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
+         check_size = (strchr(fo->VerifyOpts, 's') != NULL);
+         if ((strchr(fo->VerifyOpts, '1') != NULL) ||
+             (strchr(fo->VerifyOpts, '5') != NULL))
+         {
+            check_chksum = true;
+         } 
+
+         if (fo->flags & FO_MD5) {
+            digesttype = CRYPTO_DIGEST_MD5;
+            return;
+         }
+         if (fo->flags & FO_SHA1) {
+            digesttype = CRYPTO_DIGEST_SHA1;
+            return;
+         }
+         if (fo->flags & FO_SHA256) {
+            digesttype = CRYPTO_DIGEST_SHA256;
+            return;
+         }
+         if (fo->flags & FO_SHA512) {
+            digesttype = CRYPTO_DIGEST_SHA512;
+            return;
+         }
+      }
+   }
+   digesttype = CRYPTO_DIGEST_NONE;
+   if (check_chksum) {
+      Jmsg(jcr, M_WARNING, 0, _("Checksum verification required in Verify FileSet option, but no Signature found in the FileSet\n"));
+      check_chksum = false;
+   }
+}
+
+/* Compute the file size for sparse records and adjust the data */
+void v_ctx::skip_sparse_header(char **data, uint32_t *length)
+{
+   unser_declare;
+   uint64_t faddr;
+   unser_begin(*data, OFFSET_FADDR_SIZE);
+   unser_uint64(faddr);
+
+   /* For sparse, we assume that the file is at least big as faddr */
+   set_size(faddr);
+   
+   *data += OFFSET_FADDR_SIZE;
+   *length -= OFFSET_FADDR_SIZE;
+}
+
+void v_ctx::check_accurate()
+{
+   attr->fname = jcr->last_fname; /* struct stat is still valid, but not the fname */
+   if (accurate_check_file(jcr, attr, digest)) {
+      jcr->setJobStatus(JS_Differences);
+   }
+}
+
+/*
+ * If extracting, close any previous stream
+ */
+bool v_ctx::close_previous_stream()
+{
+   bool rtn = true;
+   uint8_t buf[CRYPTO_DIGEST_MAX_SIZE];
+   uint32_t len = CRYPTO_DIGEST_MAX_SIZE;
+   char ed1[50], ed2[50];
+
+   /* Reset the win32 filter that strips header stream out of the file */
+   win32filter.init();
+
+   /* Check the size if possible */
+   if (check_size && size >= 0) {
+      if (attr->type == FT_REG && size != (int64_t)attr->statp.st_size) {
+         Dmsg1(50, "Size comparison failed for %s\n", jcr->last_fname);
+         Jmsg(jcr, M_INFO, 0,
+              _("   st_size  differs on \"%s\". Vol: %s File: %s\n"),
+              jcr->last_fname,
+              edit_int64(size, ed1),
+              edit_int64((int64_t)attr->statp.st_size, ed2));
+         jcr->setJobStatus(JS_Differences);
+      }
+      reset_size();
+   }
+
+   /* Compute the digest and store it */
+   *digest = 0;
+   if (jcr->crypto.digest) {
+      if (!crypto_digest_finalize(jcr->crypto.digest, buf, &len)) {
+         Dmsg1(50, "Unable to finalize digest for %s\n", jcr->last_fname);
+         rtn = false;
+
+      } else {
+         bin_to_base64(digest, sizeof(digest), (char *)buf, len, true);
+      }
+      crypto_digest_free(jcr->crypto.digest);
+      jcr->crypto.digest = NULL;
+   }
+   return rtn;
+}
 
 /*
- * Verify attributes of the requested files on the Volume
+ * Verify attributes or data of the requested files on the Volume
  *
  */
 void do_verify_volume(JCR *jcr)
 {
    BSOCK *sd, *dir;
-   POOLMEM *fname;                    /* original file name */
-   POOLMEM *lname;                    /* link name */
-   int32_t stream;
    uint32_t size;
    uint32_t VolSessionId, VolSessionTime, file_index;
-   uint32_t record_file_index;
    char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
-   int type, stat;
+   int stat;
+   int bget_ret = 0;
+   char *wbuf;                        /* write buffer */
+   uint32_t wsize;                    /* write size */
+   uint32_t rsize;                    /* read size */
+   bool msg_encrypt = false, do_check_accurate=false;
+   v_ctx vctx(jcr);
+   ATTR *attr = vctx.attr;
 
    sd = jcr->store_bsock;
    if (!sd) {
@@ -70,180 +282,288 @@ void do_verify_volume(JCR *jcr)
    }
    jcr->buf_size = sd->msglen;
 
-   fname = get_pool_memory(PM_FNAME);
-   lname = get_pool_memory(PM_FNAME);
+   /* use the same buffer size to decompress both gzip and lzo */
+   if (have_libz || have_lzo) {
+      uint32_t compress_buf_size = jcr->buf_size + 12 + ((jcr->buf_size+999) / 1000) + 100;
+      jcr->compress_buf = get_memory(compress_buf_size);
+      jcr->compress_buf_size = compress_buf_size;
+   }
+
+   GetMsg *fdmsg;
+   fdmsg = New(GetMsg(jcr, sd, rec_header, GETMSG_MAX_MSG_SIZE));
+
+   fdmsg->start_read_sock();
+   bmessage *bmsg = fdmsg->new_msg(); /* get a message, to exchange with fdmsg */
 
    /*
     * Get a record from the Storage daemon
     */
-   while (bget_msg(sd) >= 0 && !job_canceled(jcr)) {
+   while ((bget_ret = fdmsg->bget_msg(&bmsg)) >= 0 && !job_canceled(jcr)) {
+      /* Remember previous stream type */
+      vctx.prev_stream = vctx.stream;
+
       /*
        * First we expect a Stream Record Header
        */
-      if (sscanf(sd->msg, rec_header, &VolSessionId, &VolSessionTime, &file_index,
-          &stream, &size) != 5) {
-         Jmsg1(jcr, M_FATAL, 0, _("Record header scan error: %s\n"), sd->msg);
+      if (sscanf(bmsg->rbuf, rec_header, &VolSessionId, &VolSessionTime, &file_index,
+          &vctx.full_stream, &size) != 5) {
+         Jmsg1(jcr, M_FATAL, 0, _("Record header scan error: %s\n"), bmsg->rbuf);
          goto bail_out;
       }
-      Dmsg3(30, "Got hdr: FilInx=%d Stream=%d size=%d.\n", file_index, stream, size);
+      vctx.stream = vctx.full_stream & STREAMMASK_TYPE;
+      Dmsg4(30, "Got hdr: FilInx=%d FullStream=%d Stream=%d size=%d.\n",
+            file_index, vctx.full_stream, vctx.stream, size);
 
       /*
        * Now we expect the Stream Data
        */
-      if (bget_msg(sd) < 0) {
-         Jmsg1(jcr, M_FATAL, 0, _("Data record error. ERR=%s\n"), sd->bstrerror());
+      if ((bget_ret = fdmsg->bget_msg(&bmsg)) < 0) {
+         if (bget_ret != BNET_EXT_TERMINATE) {
+            Jmsg1(jcr, M_FATAL, 0, _("Data record error. ERR=%s\n"), sd->bstrerror());
+         } else {
+            /* The error has been handled somewhere else, just quit */
+         }
          goto bail_out;
       }
-      if (size != ((uint32_t)sd->msglen)) {
-         Jmsg2(jcr, M_FATAL, 0, _("Actual data size %d not same as header %d\n"), sd->msglen, size);
+      if (size != ((uint32_t)bmsg->origlen)) {
+         Jmsg2(jcr, M_FATAL, 0, _("Actual data size %d not same as header %d\n"), bmsg->origlen, size);
          goto bail_out;
       }
-      Dmsg2(30, "Got stream data %s, len=%d\n", stream_to_ascii(stream), sd->msglen);
+      Dmsg2(30, "Got stream data %s, len=%d\n", stream_to_ascii(vctx.stream), bmsg->rbuflen);
 
       /* File Attributes stream */
-      switch (stream) {
+      switch (vctx.stream) {
       case STREAM_UNIX_ATTRIBUTES:
       case STREAM_UNIX_ATTRIBUTES_EX:
-         char *ap, *lp, *fp;
-
          Dmsg0(400, "Stream=Unix Attributes.\n");
-
-         if ((int)sizeof_pool_memory(fname) < sd->msglen) {
-            fname = realloc_pool_memory(fname, sd->msglen + 1);
+         if (!vctx.close_previous_stream()) {
+            goto bail_out;
          }
-
-         if ((int)sizeof_pool_memory(lname) < sd->msglen) {
-            lname = realloc_pool_memory(lname, sd->msglen + 1);
+         if (do_check_accurate) {
+            vctx.check_accurate();
          }
-         *fname = 0;
-         *lname = 0;
+         /* Next loop, we want to check the file (or we do it with the md5) */
+         do_check_accurate = true;
 
          /*
-          * An Attributes record consists of:
-          *    File_index
-          *    Type   (FT_types)
-          *    Filename
-          *    Attributes
-          *    Link name (if file linked i.e. FT_LNK)
-          *    Extended Attributes (if Win32)
+          * Unpack attributes and do sanity check them
           */
-         if (sscanf(sd->msg, "%d %d", &record_file_index, &type) != 2) {
-            Jmsg(jcr, M_FATAL, 0, _("Error scanning record header: %s\n"), sd->msg);
-            Dmsg0(0, "\nError scanning header\n");
+         if (!unpack_attributes_record(jcr, vctx.stream,
+                                       bmsg->rbuf, bmsg->rbuflen, attr)) {
             goto bail_out;
          }
-         Dmsg2(30, "Got Attr: FilInx=%d type=%d\n", record_file_index, type);
-         ap = sd->msg;
-         while (*ap++ != ' ')         /* skip record file index */
-            ;
-         while (*ap++ != ' ')         /* skip type */
-            ;
-         /* Save filename and position to attributes */
-         fp = fname;
-         while (*ap != 0) {
-            *fp++  = *ap++;           /* copy filename to fname */
-         }
-         *fp = *ap++;                 /* terminate filename & point to attribs */
-
-         Dmsg2(100, "File=%s Attr=%s\n", fname, ap);
-         /* Skip to Link name */
-         if (type == FT_LNK || type == FT_LNKSAVED) {
-            lp = ap;
-            while (*lp++ != 0) {
-               ;
-            }
-            pm_strcat(lname, lp);        /* "save" link name */
-         } else {
-            *lname = 0;
-         }
+
+         attr->data_stream = decode_stat(attr->attr, &attr->statp,
+                                         sizeof(attr->statp), &attr->LinkFI);
+
          jcr->lock();
          jcr->JobFiles++;
          jcr->num_files_examined++;
-         pm_strcpy(jcr->last_fname, fname); /* last file examined */
+         pm_strcpy(jcr->last_fname, attr->fname); /* last file examined */
          jcr->unlock();
 
+         if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG) {
+            /*
+             * Send file attributes to Director
+             *   File_index
+             *   Stream
+             *   Verify Options
+             *   Filename (full path)
+             *   Encoded attributes
+             *   Link name (if type==FT_LNK)
+             * For a directory, link is the same as fname, but with trailing
+             * slash. For a linked file, link is the link.
+             */
+            /* Send file attributes to Director */
+            Dmsg2(200, "send ATTR inx=%d fname=%s\n", jcr->JobFiles, attr->fname);
+            if (attr->type == FT_LNK || attr->type == FT_LNKSAVED) {
+               stat = dir->fsend("%d %d %s %s%c%s%c%s%c", jcr->JobFiles,
+                                 STREAM_UNIX_ATTRIBUTES, "pinsug5", attr->fname,
+                                 0, attr->attr, 0, attr->lname, 0);
+               /* for a deleted record, we set fileindex=0 */
+            } else if (attr->type == FT_DELETED)  {
+               stat = dir->fsend("%d %d %s %s%c%s%c%c", 0,
+                                 STREAM_UNIX_ATTRIBUTES, "pinsug5", attr->fname,
+                                 0, attr->attr, 0, 0);
+            } else {
+               stat = dir->fsend("%d %d %s %s%c%s%c%c", jcr->JobFiles,
+                                 STREAM_UNIX_ATTRIBUTES, "pinsug5", attr->fname,
+                                 0, attr->attr, 0, 0);
+            }
+            Dmsg2(200, "bfiled>bdird: attribs len=%d: msg=%s\n", dir->msglen, dir->msg);
+            if (!stat) {
+               Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), dir->bstrerror());
+               goto bail_out;
+            }
+         }
+         break;
+
          /*
-          * Send file attributes to Director
-          *   File_index
-          *   Stream
-          *   Verify Options
-          *   Filename (full path)
-          *   Encoded attributes
-          *   Link name (if type==FT_LNK)
-          * For a directory, link is the same as fname, but with trailing
-          * slash. For a linked file, link is the link.
+          * Restore stream object is counted, but not restored here
           */
-         /* Send file attributes to Director */
-         Dmsg2(200, "send ATTR inx=%d fname=%s\n", jcr->JobFiles, fname);
-         if (type == FT_LNK || type == FT_LNKSAVED) {
-            stat = dir->fsend("%d %d %s %s%c%s%c%s%c", jcr->JobFiles,
-                          STREAM_UNIX_ATTRIBUTES, "pinsug5", fname,
-                          0, ap, 0, lname, 0);
-         /* for a deleted record, we set fileindex=0 */
-         } else if (type == FT_DELETED)  {
-            stat = dir->fsend("%d %d %s %s%c%s%c%c", 0,
-                          STREAM_UNIX_ATTRIBUTES, "pinsug5", fname,
-                          0, ap, 0, 0);
-         } else {
-            stat = dir->fsend("%d %d %s %s%c%s%c%c", jcr->JobFiles,
-                          STREAM_UNIX_ATTRIBUTES, "pinsug5", fname,
-                          0, ap, 0, 0);
-         }
-         Dmsg2(200, "bfiled>bdird: attribs len=%d: msg=%s\n", dir->msglen, dir->msg);
-         if (!stat) {
-            Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), dir->bstrerror());
-            goto bail_out;
-         }
+      case STREAM_RESTORE_OBJECT:
+         jcr->lock();
+         jcr->JobFiles++;
+         jcr->num_files_examined++;
+         jcr->unlock();
          break;
 
+      default:
+         break;
+      }
+
+      const char *digest_code = NULL;
+
+      switch(vctx.stream) {
       case STREAM_MD5_DIGEST:
-         bin_to_base64(digest, sizeof(digest), (char *)sd->msg, CRYPTO_DIGEST_MD5_SIZE, true);
-         Dmsg2(400, "send inx=%d MD5=%s\n", jcr->JobFiles, digest);
-         dir->fsend("%d %d %s *MD5-%d*", jcr->JobFiles, STREAM_MD5_DIGEST, digest,
-                    jcr->JobFiles);
-         Dmsg2(20, "bfiled>bdird: MD5 len=%d: msg=%s\n", dir->msglen, dir->msg);
+         bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_MD5_SIZE, true);
+         digest_code = "MD5";
          break;
 
       case STREAM_SHA1_DIGEST:
-         bin_to_base64(digest, sizeof(digest), (char *)sd->msg, CRYPTO_DIGEST_SHA1_SIZE, true);
-         Dmsg2(400, "send inx=%d SHA1=%s\n", jcr->JobFiles, digest);
-         dir->fsend("%d %d %s *SHA1-%d*", jcr->JobFiles, STREAM_SHA1_DIGEST,
-                    digest, jcr->JobFiles);
-         Dmsg2(20, "bfiled>bdird: SHA1 len=%d: msg=%s\n", dir->msglen, dir->msg);
+         bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_SHA1_SIZE, true);
+         digest_code = "SHA1";
          break;
 
       case STREAM_SHA256_DIGEST:
-         bin_to_base64(digest, sizeof(digest), (char *)sd->msg, CRYPTO_DIGEST_SHA256_SIZE, true);
-         Dmsg2(400, "send inx=%d SHA256=%s\n", jcr->JobFiles, digest);
-         dir->fsend("%d %d %s *SHA256-%d*", jcr->JobFiles, STREAM_SHA256_DIGEST,
-                    digest, jcr->JobFiles);
-         Dmsg2(20, "bfiled>bdird: SHA256 len=%d: msg=%s\n", dir->msglen, dir->msg);
+         bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_SHA256_SIZE, true);
+         digest_code = "SHA256";
          break;
 
       case STREAM_SHA512_DIGEST:
-         bin_to_base64(digest, sizeof(digest), (char *)sd->msg, CRYPTO_DIGEST_SHA512_SIZE, true);
-         Dmsg2(400, "send inx=%d SHA512=%s\n", jcr->JobFiles, digest);
-         dir->fsend("%d %d %s *SHA512-%d*", jcr->JobFiles, STREAM_SHA512_DIGEST,
-                    digest, jcr->JobFiles);
-         Dmsg2(20, "bfiled>bdird: SHA512 len=%d: msg=%s\n", dir->msglen, dir->msg);
-         break;
-
-      /*
-       * Restore stream object is counted, but not restored here
-       */
-      case STREAM_RESTORE_OBJECT:
-         jcr->lock();
-         jcr->JobFiles++;
-         jcr->num_files_examined++;
-         jcr->unlock();
+         bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_SHA512_SIZE, true);
+         digest_code = "SHA512";
          break;
 
-      /* Ignore everything else */
       default:
+         *digest = 0;
          break;
+      }
+
+      if (digest_code && jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG) {
+         dir->fsend("%d %d %s *%s-%d*", jcr->JobFiles, vctx.stream,
+                    digest, digest_code, jcr->JobFiles);
+
+      } else if (jcr->getJobLevel() == L_VERIFY_DATA) {
+         /* Compare digest */
+         if (vctx.check_chksum && *digest) {
+            /* probably an empty file, we can create an empty crypto session */
+            if (!jcr->crypto.digest) {
+               jcr->crypto.digest = crypto_digest_new(jcr, vctx.digesttype);
+            }
+            vctx.close_previous_stream();
+            if (strncmp(digest, vctx.digest,
+                        MIN(sizeof(digest), sizeof(vctx.digest))) != 0)
+            {
+               Jmsg(jcr, M_INFO, 0,
+                    _("   %s differs on \"%s\". File=%s Vol=%s\n"),
+                    stream_to_ascii(vctx.stream), jcr->last_fname,
+                    vctx.digest, digest);
+               jcr->setJobStatus(JS_Differences);
+               Dmsg3(50, "Signature verification failed for %s %s != %s\n",
+                     jcr->last_fname, digest, vctx.digest);
+            }
+            if (do_check_accurate) {
+               vctx.check_accurate();
+               do_check_accurate = false; /* Don't do it in the next loop */
+            }
+         }
+
+         /* Compute size and checksum for level=Data */
+         switch (vctx.stream) {
+         case STREAM_ENCRYPTED_FILE_DATA:
+         case STREAM_ENCRYPTED_WIN32_DATA:
+         case STREAM_ENCRYPTED_FILE_GZIP_DATA:
+         case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
+         case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
+         case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
+            if (!msg_encrypt) {
+               Jmsg(jcr, M_WARNING, 0,
+                  _("Verification of encrypted file data is not supported.\n"));
+               msg_encrypt = true;
+            }
+            break;
+
+         case STREAM_PLUGIN_DATA:
+         case STREAM_FILE_DATA:
+         case STREAM_SPARSE_DATA:
+         case STREAM_WIN32_DATA:
+         case STREAM_GZIP_DATA:
+         case STREAM_SPARSE_GZIP_DATA:
+         case STREAM_WIN32_GZIP_DATA:
+         case STREAM_COMPRESSED_DATA:
+         case STREAM_SPARSE_COMPRESSED_DATA:
+         case STREAM_WIN32_COMPRESSED_DATA:
+            if (!(attr->type ==  FT_RAW || attr->type == FT_FIFO || attr->type == FT_REG || attr->type == FT_REGE)) {
+               break;
+            }
+
+            wbuf = bmsg->rbuf;
+            rsize = bmsg->rbuflen;
+            jcr->ReadBytes += rsize;
+            wsize = rsize;
+
+            if (vctx.stream == STREAM_SPARSE_DATA
+                || vctx.stream == STREAM_SPARSE_COMPRESSED_DATA
+                || vctx.stream == STREAM_SPARSE_GZIP_DATA) {
+               vctx.skip_sparse_header(&wbuf, &wsize);
+            }
+
+            if (vctx.stream == STREAM_GZIP_DATA
+                || vctx.stream == STREAM_SPARSE_GZIP_DATA
+                || vctx.stream == STREAM_WIN32_GZIP_DATA
+                || vctx.stream == STREAM_ENCRYPTED_FILE_GZIP_DATA
+                || vctx.stream == STREAM_COMPRESSED_DATA
+                || vctx.stream == STREAM_SPARSE_COMPRESSED_DATA
+                || vctx.stream == STREAM_WIN32_COMPRESSED_DATA
+                || vctx.stream == STREAM_ENCRYPTED_FILE_COMPRESSED_DATA
+                || vctx.stream == STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA
+                || vctx.stream == STREAM_ENCRYPTED_WIN32_GZIP_DATA) {
+
+               if (!decompress_data(jcr, vctx.stream, &wbuf, &wsize)) {
+                  dequeue_messages(jcr);
+                  goto bail_out;
+               }
+            }
+
+            vctx.update_checksum(wbuf, wsize);
 
+            if (vctx.stream == STREAM_WIN32_GZIP_DATA
+                || vctx.stream == STREAM_WIN32_DATA
+                || vctx.stream == STREAM_WIN32_COMPRESSED_DATA
+                || vctx.stream == STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA
+                || vctx.stream == STREAM_ENCRYPTED_WIN32_GZIP_DATA) {
+
+               int64_t wbuf_len = wsize;
+               int64_t wsize64 = 0;
+               if (vctx.win32filter.have_data(&wbuf, &wbuf_len, &wsize64)) {
+                  wsize = wsize64;
+               }
+            }
+            jcr->JobBytes += wsize;
+            vctx.update_size(wsize);
+            break;
+
+            /* TODO: Handle data to compute checksums */
+            /* Ignore everything else */
+         default:
+            break;
+         }
       } /* end switch */
    } /* end while bnet_get */
+   if (bget_ret == BNET_EXT_TERMINATE) {
+      goto bail_out;
+   }
+   if (!vctx.close_previous_stream()) {
+      goto bail_out;
+   }
+   /* Check the last file */
+   if (do_check_accurate) {
+      vctx.check_accurate();
+   }
+   if (!accurate_finish(jcr)) {
+      goto bail_out;
+   }
    jcr->setJobStatus(JS_Terminated);
    goto ok_out;
 
@@ -251,12 +571,14 @@ bail_out:
    jcr->setJobStatus(JS_ErrorTerminated);
 
 ok_out:
+   fdmsg->wait_read_sock(jcr->is_job_canceled());
+   delete bmsg;
+   free_GetMsg(fdmsg);
    if (jcr->compress_buf) {
-      free(jcr->compress_buf);
+      free_pool_memory(jcr->compress_buf);
       jcr->compress_buf = NULL;
    }
-   free_pool_memory(fname);
-   free_pool_memory(lname);
-   Dmsg2(050, "End Verify-Vol. Files=%d Bytes=%" lld "\n", jcr->JobFiles,
+   /* TODO: We probably want to mark the job as failed if we have errors */
+   Dmsg2(50, "End Verify-Vol. Files=%d Bytes=%" lld "\n", jcr->JobFiles,
       jcr->JobBytes);
 }