]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/filed/accurate.c
Big backport from Enterprise
[bacula/bacula] / bacula / src / filed / accurate.c
index 83574d323e0a18ea1a85b2cb70e9be95f95f8470..94911ef571cc0a26f3e2896afcf0c7c175ea4f80 100644 (file)
@@ -1,37 +1,25 @@
 /*
-   Bacula® - The Network Backup Solution
-
-   Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
-
-   The main author of Bacula is Kern Sibbald, with contributions from
-   many others, a complete list can be found in the file AUTHORS.
-   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 Kern Sibbald.
-   The licensor of Bacula is the Free Software Foundation Europe
-   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
-   Switzerland, email:ftf@fsfeurope.org.
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   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.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
 */
-/*
- *  Version $Id $
- *
- */
 
 #include "bacula.h"
 #include "filed.h"
+#include "backup.h"
 
 static int dbglvl=100;
 
@@ -40,6 +28,7 @@ typedef struct PrivateCurFile {
    char *fname;
    char *lstat;
    char *chksum;
+   int32_t delta_seq;
    bool seen;
 } CurFile;
 
@@ -97,10 +86,13 @@ static bool accurate_send_base_file_list(JCR *jcr)
    CurFile *elt;
    struct stat statc;
    int32_t LinkFIc;
-   FF_PKT *ff_pkt;
-   int stream = STREAM_UNIX_ATTRIBUTES;
+   bctx_t bctx;
+
+   memset(&bctx, 0, sizeof(bctx));
+   bctx.jcr = jcr;
+   bctx.data_stream = STREAM_UNIX_ATTRIBUTES;
 
-   if (!jcr->accurate || jcr->get_JobLevel() != L_FULL) {
+   if (!jcr->accurate || jcr->getJobLevel() != L_FULL) {
       return true;
    }
 
@@ -108,22 +100,22 @@ static bool accurate_send_base_file_list(JCR *jcr)
       return true;
    }
 
-   ff_pkt = init_find_files();
-   ff_pkt->type = FT_BASE;
+   bctx.ff_pkt = init_find_files();
+   bctx.ff_pkt->type = FT_BASE;
 
    foreach_htable(elt, jcr->file_list) {
       if (elt->seen) {
          Dmsg2(dbglvl, "base file fname=%s seen=%i\n", elt->fname, elt->seen);
          /* TODO: skip the decode and use directly the lstat field */
-         decode_stat(elt->lstat, &statc, &LinkFIc); /* decode catalog stat */  
-         ff_pkt->fname = elt->fname;
-         ff_pkt->statp = statc;
-         encode_and_send_attributes(jcr, ff_pkt, stream);
+         decode_stat(elt->lstat, &statc, sizeof(statc), &LinkFIc); /* decode catalog stat */
+         bctx.ff_pkt->fname = elt->fname;
+         bctx.ff_pkt->statp = statc;
+         encode_and_send_attributes(bctx);
 //       free(elt->fname);
       }
    }
 
-   term_find_files(ff_pkt);
+   term_find_files(bctx.ff_pkt);
    return true;
 }
 
@@ -137,8 +129,11 @@ static bool accurate_send_deleted_list(JCR *jcr)
    CurFile *elt;
    struct stat statc;
    int32_t LinkFIc;
-   FF_PKT *ff_pkt;
-   int stream = STREAM_UNIX_ATTRIBUTES;
+   bctx_t bctx;
+
+   memset(&bctx, 0, sizeof(bctx));
+   bctx.jcr = jcr;
+   bctx.data_stream = STREAM_UNIX_ATTRIBUTES;
 
    if (!jcr->accurate) {
       return true;
@@ -148,8 +143,8 @@ static bool accurate_send_deleted_list(JCR *jcr)
       return true;
    }
 
-   ff_pkt = init_find_files();
-   ff_pkt->type = FT_DELETED;
+   bctx.ff_pkt = init_find_files();
+   bctx.ff_pkt->type = FT_DELETED;
 
    foreach_htable(elt, jcr->file_list) {
       if (elt->seen || plugin_check_file(jcr, elt->fname)) {
@@ -157,18 +152,49 @@ static bool accurate_send_deleted_list(JCR *jcr)
       }
       Dmsg2(dbglvl, "deleted fname=%s seen=%i\n", elt->fname, elt->seen);
       /* TODO: skip the decode and use directly the lstat field */
-      decode_stat(elt->lstat, &statc, &LinkFIc); /* decode catalog stat */
-      ff_pkt->fname = elt->fname;
-      ff_pkt->statp.st_mtime = statc.st_mtime;
-      ff_pkt->statp.st_ctime = statc.st_ctime;
-      encode_and_send_attributes(jcr, ff_pkt, stream);
+      decode_stat(elt->lstat, &statc, sizeof(statc), &LinkFIc); /* decode catalog stat */
+      bctx.ff_pkt->fname = elt->fname;
+      bctx.ff_pkt->statp.st_mtime = statc.st_mtime;
+      bctx.ff_pkt->statp.st_ctime = statc.st_ctime;
+      encode_and_send_attributes(bctx);
 //    free(elt->fname);
    }
 
-   term_find_files(ff_pkt);
+   term_find_files(bctx.ff_pkt);
    return true;
 }
 
+
+/* This function is called at the end of verify job
+ * We walk over all hash disk element, and we check
+ * for elt.seen.
+ */
+static bool accurate_check_deleted_list(JCR *jcr)
+{
+   bool ret=true;
+   CurFile *elt;
+
+   if (!jcr->accurate) {
+      return true;
+   }
+
+   if (jcr->file_list == NULL) {
+      return true;
+   }
+
+   foreach_htable(elt, jcr->file_list) {
+      if (elt->seen) {
+         continue;
+      }
+      if (ret) {
+         Jmsg(jcr, M_INFO, 0, _("The following files were in the Catalog, but not in the Job data:\n"), elt->fname);
+      }
+      ret = false;
+      Jmsg(jcr, M_INFO, 0, _("    %s\n"), elt->fname);
+   }
+   return ret;
+}
+
 void accurate_free(JCR *jcr)
 {
    if (jcr->file_list) {
@@ -181,35 +207,46 @@ void accurate_free(JCR *jcr)
 /* Send the deleted or the base file list and cleanup  */
 bool accurate_finish(JCR *jcr)
 {
-   bool ret=true;
+   bool ret = true;
+
+   if (jcr->is_canceled() || jcr->is_incomplete()) {
+      accurate_free(jcr);
+      return ret;
+   }
    if (jcr->accurate) {
-      if (jcr->get_JobLevel() == L_FULL) {
-         ret = accurate_send_base_file_list(jcr);
+      if (jcr->is_JobLevel(L_FULL)) {
+         if (!jcr->rerunning) {
+            ret = accurate_send_base_file_list(jcr);
+         }
+      } else if (jcr->is_JobLevel(L_VERIFY_DATA)) {
+         ret = accurate_check_deleted_list(jcr);
+
       } else {
          ret = accurate_send_deleted_list(jcr);
       }
-      
       accurate_free(jcr);
-      if (jcr->get_JobLevel() == L_FULL) {
-         Jmsg(jcr, M_INFO, 0, _("Space saved with Base jobs: %lld MB\n"), 
+      if (jcr->is_JobLevel(L_FULL)) {
+         Jmsg(jcr, M_INFO, 0, _("Space saved with Base jobs: %lld MB\n"),
               jcr->base_size/(1024*1024));
       }
    }
    return ret;
 }
 
-static bool accurate_add_file(JCR *jcr, uint32_t len, 
-                              char *fname, char *lstat, char *chksum)
+static bool accurate_add_file(JCR *jcr, uint32_t len,
+                              char *fname, char *lstat, char *chksum,
+                              int32_t delta)
 {
    bool ret = true;
-   CurFile elt;
-   elt.seen = 0;
-
    CurFile *item;
-   /* we store CurFile, fname and ctime/mtime in the same chunk */
+
+   /* we store CurFile, fname and ctime/mtime in the same chunk
+    * we need one extra byte to handle an empty chksum
+    */
    item = (CurFile *)jcr->file_list->hash_malloc(sizeof(CurFile)+len+3);
-   memcpy(item, &elt, sizeof(CurFile));
+   item->seen = 0;
 
+   /* TODO: see if we can optimize this part with memcpy instead of strcpy */
    item->fname  = (char *)item+sizeof(CurFile);
    strcpy(item->fname, fname);
 
@@ -219,17 +256,74 @@ static bool accurate_add_file(JCR *jcr, uint32_t len,
    item->chksum = item->lstat+strlen(item->lstat)+1;
    strcpy(item->chksum, chksum);
 
-   jcr->file_list->insert(item->fname, item); 
+   item->delta_seq = delta;
 
-   Dmsg3(dbglvl, "add fname=<%s> lstat=%s chksum=%s\n", fname, lstat, chksum);
+   jcr->file_list->insert(item->fname, item);
+
+   Dmsg4(dbglvl, "add fname=<%s> lstat=%s  delta_seq=%i chksum=%s\n",
+         fname, lstat, delta, chksum);
    return ret;
 }
 
+bool accurate_check_file(JCR *jcr, ATTR *attr, char *digest)
+{
+   struct stat statc;
+   int32_t LinkFIc;
+   bool stat = false;
+   char ed1[50], ed2[50];
+   CurFile elt;
+
+   if (!jcr->accurate) {
+      goto bail_out;
+   }
+
+   if (!jcr->file_list) {
+      goto bail_out;             /* Not initialized properly */
+   }
+
+   if (!accurate_lookup(jcr, attr->fname, &elt)) {
+      Dmsg1(dbglvl, "accurate %s (not found)\n", attr->fname);
+      stat = true;
+      goto bail_out;
+   }
+   decode_stat(elt.lstat, &statc, sizeof(statc), &LinkFIc); /* decode catalog stat */
+
+   /*
+    * Loop over options supplied by user and verify the
+    * fields he requests.
+    */
+   if (statc.st_size != attr->statp.st_size) {
+      Dmsg3(50, "%s      st_size  differs. Cat: %s File: %s\n",
+            attr->fname,
+            edit_uint64((uint64_t)statc.st_size, ed1),
+            edit_uint64((uint64_t)attr->statp.st_size, ed2));
+      Jmsg(jcr, M_INFO, 0, "Cat st_size differs: %s\n", attr->fname);
+      stat = true;
+   }
+
+   if (*elt.chksum && digest && *digest) {
+      if (strcmp(digest, elt.chksum)) {
+         Dmsg3(50, "%s      chksum  differs. Cat: %s File: %s\n",
+               attr->fname,
+               elt.chksum,
+               digest);
+         Jmsg(jcr, M_INFO, 0, "Cat checksum differs: %s\n", attr->fname);
+         stat = true;
+      }
+   }
+
+   accurate_mark_file_as_seen(jcr, &elt);
+
+bail_out:
+   return stat;
+
+}
+
 /*
  * This function is called for each file seen in fileset.
  * We check in file_list hash if fname have been backuped
- * the last time. After we can compare Lstat field. 
- * Full Lstat usage have been removed on 6612 
+ * the last time. After we can compare Lstat field.
+ * Full Lstat usage have been removed on 6612
  *
  * Returns: true   if file has changed (must be backed up)
  *          false  file not changed
@@ -246,32 +340,39 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
    char *fname;
    CurFile elt;
 
-   if (!jcr->accurate) {
+   ff_pkt->delta_seq = 0;
+   ff_pkt->accurate_found = false;
+
+   if (!jcr->accurate && !jcr->rerunning) {
       return true;
    }
 
+   if (!jcr->file_list) {
+      return true;              /* Not initialized properly */
+   }
+
    strip_path(ff_pkt);
+
    if (S_ISDIR(ff_pkt->statp.st_mode)) {
       fname = ff_pkt->link;
    } else {
       fname = ff_pkt->fname;
-   } 
+   }
 
    if (!accurate_lookup(jcr, fname, &elt)) {
       Dmsg1(dbglvl, "accurate %s (not found)\n", fname);
       stat = true;
+      unstrip_path(ff_pkt);
       goto bail_out;
    }
 
-   if (elt.seen) { /* file has been seen ? */
-      Dmsg1(dbglvl, "accurate %s (already seen)\n", fname);
-      goto bail_out;
-   }
+   unstrip_path(ff_pkt);     /* Get full path back */
+   ff_pkt->accurate_found = true;
+   ff_pkt->delta_seq = elt.delta_seq;
 
-   decode_stat(elt.lstat, &statc, &LinkFIc); /* decode catalog stat */
+   decode_stat(elt.lstat, &statc, sizeof(statc), &LinkFIc); /* decode catalog stat */
 
-   if (jcr->get_JobLevel() == L_FULL) {
+   if (!jcr->rerunning && (jcr->getJobLevel() == L_FULL)) {
       opts = ff_pkt->BaseJobOpts;
    } else {
       opts = ff_pkt->AccurateOpts;
@@ -349,6 +450,16 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
             stat = true;
          }
          break;
+      case 'M':                 /* Look mtime/ctime like normal incremental backup */
+         if (ff_pkt->incremental &&
+             (ff_pkt->statp.st_mtime > ff_pkt->save_time &&
+              ((ff_pkt->flags & FO_MTIMEONLY) ||
+               ff_pkt->statp.st_ctime > ff_pkt->save_time)))
+         {
+            Dmsg1(dbglvl-1, "%s      mtime/ctime more recent than save_time\n", fname);
+            stat = true;
+         }
+         break;
       case 'c':                /* ctime */
          if (statc.st_ctime != ff_pkt->statp.st_ctime) {
             Dmsg1(dbglvl-1, "%s      st_ctime differs\n", fname);
@@ -364,21 +475,23 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
             stat = true;
          }
          break;
-
+      case 'A':                 /* Always backup a file */
+         stat = true;
+         break;
       /* TODO: cleanup and factorise this function with verify.c */
       case '5':                /* compare MD5 */
-      case '1':                 /* compare SHA1 */
+      case '1':                /* compare SHA1 */
         /*
           * The remainder of the function is all about getting the checksum.
           * First we initialise, then we read files, other streams and Finder Info.
           */
-         if (!stat && ff_pkt->type != FT_LNKSAVED && 
-             (S_ISREG(ff_pkt->statp.st_mode) && 
-              ff_pkt->flags & (FO_MD5|FO_SHA1|FO_SHA256|FO_SHA512))) 
+         if (!stat && ff_pkt->type != FT_LNKSAVED &&
+             (S_ISREG(ff_pkt->statp.st_mode) &&
+              ff_pkt->flags & (FO_MD5|FO_SHA1|FO_SHA256|FO_SHA512)))
          {
 
-            if (!*elt.chksum) {
-               Jmsg(jcr, M_WARNING, 0, _("Can't verify checksum for %s\n"),
+            if (!*elt.chksum && !jcr->rerunning) {
+               Jmsg(jcr, M_WARNING, 0, _("Cannot verify checksum for %s\n"),
                     ff_pkt->fname);
                stat = true;
                break;
@@ -391,20 +504,20 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
             if (ff_pkt->flags & FO_MD5) {
                digest = crypto_digest_new(jcr, CRYPTO_DIGEST_MD5);
                digest_stream = STREAM_MD5_DIGEST;
-               
+
             } else if (ff_pkt->flags & FO_SHA1) {
                digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA1);
                digest_stream = STREAM_SHA1_DIGEST;
-               
+
             } else if (ff_pkt->flags & FO_SHA256) {
                digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA256);
                digest_stream = STREAM_SHA256_DIGEST;
-               
+
             } else if (ff_pkt->flags & FO_SHA512) {
                digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA512);
                digest_stream = STREAM_SHA512_DIGEST;
             }
-            
+
             /* Did digest initialization fail? */
             if (digest_stream != STREAM_NONE && digest == NULL) {
                Jmsg(jcr, M_WARNING, 0, _("%s digest initialization failed\n"),
@@ -415,29 +528,30 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
             if (digest) {
                char md[CRYPTO_DIGEST_MAX_SIZE];
                uint32_t size;
-               
+
                size = sizeof(md);
-               
+
                if (digest_file(jcr, ff_pkt, digest) != 0) {
                   jcr->JobErrors++;
 
                } else if (crypto_digest_finalize(digest, (uint8_t *)md, &size)) {
                   char *digest_buf;
                   const char *digest_name;
-                  
+
                   digest_buf = (char *)malloc(BASE64_SIZE(size));
                   digest_name = crypto_digest_name(digest);
-                  
+
                   bin_to_base64(digest_buf, BASE64_SIZE(size), md, size, true);
 
                   if (strcmp(digest_buf, elt.chksum)) {
-                     Dmsg3(dbglvl-1, "%s      chksum  diff. Cat: %s File: %s\n",
+                     Dmsg4(dbglvl,"%s      %s chksum  diff. Cat: %s File: %s\n",
                            fname,
+                           digest_name,
                            elt.chksum,
                            digest_buf);
                      stat = true;
                   }
-                  
+
                   free(digest_buf);
                }
                crypto_digest_free(digest);
@@ -456,8 +570,8 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
    /* In Incr/Diff accurate mode, we mark all files as seen
     * When in Full+Base mode, we mark only if the file match exactly
     */
-   if (jcr->get_JobLevel() == L_FULL) {
-      if (!stat) {               
+   if (jcr->getJobLevel() == L_FULL) {
+      if (!stat) {
          /* compute space saved with basefile */
          jcr->base_size += ff_pkt->statp.st_size;
          accurate_mark_file_as_seen(jcr, &elt);
@@ -467,11 +581,10 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
    }
 
 bail_out:
-   unstrip_path(ff_pkt);
    return stat;
 }
 
-/* 
+/*
  * TODO: use big buffer from htable
  */
 int accurate_cmd(JCR *jcr)
@@ -479,6 +592,7 @@ int accurate_cmd(JCR *jcr)
    BSOCK *dir = jcr->dir_bsock;
    int lstat_pos, chksum_pos;
    int32_t nb;
+   uint16_t delta_seq;
 
    if (job_canceled(jcr)) {
       return true;
@@ -494,7 +608,7 @@ int accurate_cmd(JCR *jcr)
 
    /*
     * buffer = sizeof(CurFile) + dirmsg
-    * dirmsg = fname + \0 + lstat + \0 + checksum + \0
+    * dirmsg = fname + \0 + lstat + \0 + checksum + \0 + delta_seq + \0
     */
    /* get current files */
    while (dir->recv() >= 0) {
@@ -504,12 +618,18 @@ int accurate_cmd(JCR *jcr)
 
          if (chksum_pos >= dir->msglen) {
             chksum_pos = lstat_pos - 1;    /* tweak: no checksum, point to the last \0 */
-         } 
+            delta_seq = 0;
+         } else {
+            delta_seq = str_to_int32(dir->msg +
+                                     chksum_pos +
+                                     strlen(dir->msg + chksum_pos) + 1);
+         }
 
-         accurate_add_file(jcr, dir->msglen, 
+         accurate_add_file(jcr, dir->msglen,
                            dir->msg,               /* Path */
                            dir->msg + lstat_pos,   /* LStat */
-                           dir->msg + chksum_pos); /* CheckSum */
+                           dir->msg + chksum_pos,  /* CheckSum */
+                           delta_seq);             /* Delta Sequence */
       }
    }