]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/filed/accurate.c
Use RestoreObject type in Catalog
[bacula/bacula] / bacula / src / filed / accurate.c
index 1541fa889d7684ad60f7f698fc9b1fb5d62e3e4b..404756aa6c20e16e42b1880c2848877abc97e145 100644 (file)
@@ -1,12 +1,12 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2011 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
+   modify it under the terms of version three of the GNU Affero General Public
    License as published by the Free Software Foundation and included
    in the file LICENSE.
 
@@ -15,7 +15,7 @@
    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
+   You should have received a copy of the GNU Affero 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.
@@ -39,6 +39,8 @@ typedef struct PrivateCurFile {
    hlink link;
    char *fname;
    char *lstat;
+   char *chksum;
+   int32_t delta_seq;
    bool seen;
 } CurFile;
 
@@ -99,7 +101,7 @@ static bool accurate_send_base_file_list(JCR *jcr)
    FF_PKT *ff_pkt;
    int stream = STREAM_UNIX_ATTRIBUTES;
 
-   if (!jcr->accurate || jcr->get_JobLevel() != L_FULL) {
+   if (!jcr->accurate || jcr->getJobLevel() != L_FULL) {
       return true;
    }
 
@@ -114,7 +116,7 @@ static bool accurate_send_base_file_list(JCR *jcr)
       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 */  
+         decode_stat(elt->lstat, &statc, sizeof(statc), &LinkFIc); /* decode catalog stat */  
          ff_pkt->fname = elt->fname;
          ff_pkt->statp = statc;
          encode_and_send_attributes(jcr, ff_pkt, stream);
@@ -156,7 +158,7 @@ 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 */
+      decode_stat(elt->lstat, &statc, sizeof(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;
@@ -180,40 +182,58 @@ 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 {
          ret = accurate_send_deleted_list(jcr);
       }
-      
       accurate_free(jcr);
-      if (jcr->get_JobLevel() == L_FULL) {
-         Dmsg1(0, "Space saved with Base jobs: %lld MB\n"
-               jcr->base_size/(1024*1024));
+      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, char *fname, char *lstat)
+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 */
-   item = (CurFile *)jcr->file_list->hash_malloc(sizeof(CurFile)+strlen(fname)+strlen(lstat)+2);
-   memcpy(item, &elt, sizeof(CurFile));
+
+   /* 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+1);
+   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);
+
    item->lstat  = item->fname+strlen(item->fname)+1;
    strcpy(item->lstat, lstat);
+
+   item->chksum = item->lstat+strlen(item->lstat)+1;
+   strcpy(item->chksum, chksum);
+
+   item->delta_seq = delta;
+
    jcr->file_list->insert(item->fname, item); 
 
-   Dmsg2(dbglvl, "add fname=<%s> lstat=%s\n", fname, lstat);
+   Dmsg4(dbglvl, "add fname=<%s> lstat=%s  delta_seq=%i chksum=%s\n", 
+         fname, lstat, delta, chksum);
    return ret;
 }
 
@@ -228,13 +248,19 @@ static bool accurate_add_file(JCR *jcr, char *fname, char *lstat)
  */
 bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
 {
+   int digest_stream = STREAM_NONE;
+   DIGEST *digest = NULL;
+
    struct stat statc;
    int32_t LinkFIc;
    bool stat = false;
+   char *opts;
    char *fname;
    CurFile elt;
 
-   if (!jcr->accurate) {
+   ff_pkt->delta_seq = 0;
+
+   if (!jcr->accurate && !jcr->rerunning) {
       return true;
    }
 
@@ -252,19 +278,26 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
       goto bail_out;
    }
 
+   ff_pkt->delta_seq = elt.delta_seq;
+
    if (elt.seen) { /* file has been seen ? */
       Dmsg1(dbglvl, "accurate %s (already seen)\n", fname);
       goto bail_out;
    }
 
-   decode_stat(elt.lstat, &statc, &LinkFIc); /* decode catalog stat */
+   decode_stat(elt.lstat, &statc, sizeof(statc), &LinkFIc); /* decode catalog stat */
+
+   if (!jcr->rerunning && (jcr->getJobLevel() == L_FULL)) {
+      opts = ff_pkt->BaseJobOpts;
+   } else {
+      opts = ff_pkt->AccurateOpts;
+   }
 
-//#if 0
    /*
     * Loop over options supplied by user and verify the
     * fields he requests.
     */
-   for (char *p=ff_pkt->AccurateOpts; *p; p++) {
+   for (char *p=opts; !stat && *p; p++) {
       char ed1[30], ed2[30];
       switch (*p) {
       case 'i':                /* compare INODEs */
@@ -277,6 +310,9 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
          }
          break;
       case 'p':                /* permissions bits */
+         /* TODO: If something change only in perm, user, group
+          * Backup only the attribute stream
+          */
          if (statc.st_mode != ff_pkt->statp.st_mode) {
             Dmsg3(dbglvl-1, "%s     st_mode  differ. Cat: %x File: %x\n",
                   fname,
@@ -323,7 +359,7 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
             stat = true;
          }
          break;
-      case 'm':
+      case 'm':                 /* modification time */
          if (statc.st_mtime != ff_pkt->statp.st_mtime) {
             Dmsg1(dbglvl-1, "%s      st_mtime differs\n", fname);
             stat = true;
@@ -331,7 +367,7 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
          break;
       case 'c':                /* ctime */
          if (statc.st_ctime != ff_pkt->statp.st_ctime) {
-            Dmsg1(dbglvl-1, "      st_ctime differs\n", fname);
+            Dmsg1(dbglvl-1, "%s      st_ctime differs\n", fname);
             stat = true;
          }
          break;
@@ -344,44 +380,102 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
             stat = true;
          }
          break;
-      case '5':                /* compare MD5 */
+      case 'A':                 /* Always backup a file */
+         stat = true;
          break;
-      case '1':                 /* compare SHA1 */
+      /* TODO: cleanup and factorise this function with verify.c */
+      case '5':                /* compare MD5 */
+      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 (!*elt.chksum && !jcr->rerunning) {
+               Jmsg(jcr, M_WARNING, 0, _("Cannot verify checksum for %s\n"),
+                    ff_pkt->fname);
+               stat = true;
+               break;
+            }
+
+            /*
+             * Create our digest context. If this fails, the digest will be set
+             * to NULL and not used.
+             */
+            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"),
+                    stream_to_ascii(digest_stream));
+            }
+
+            /* compute MD5 or SHA1 hash */
+            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)) {
+                     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);
+            }
+         }
+
          break;
       case ':':
+      case 'J':
       case 'C':
       default:
          break;
-            }
-   }
-//#endif
-#if 0
-   /*
-    * We check only mtime/ctime like with the normal
-    * incremental/differential mode
-    */
-   if (statc.st_mtime != ff_pkt->statp.st_mtime) {
-//   Jmsg(jcr, M_SAVED, 0, _("%s      st_mtime differs\n"), fname);
-      Dmsg3(dbglvl, "%s      st_mtime differs (%lld!=%lld)\n", 
-            fname, statc.st_mtime, (utime_t)ff_pkt->statp.st_mtime);
-     stat = true;
-   } else if (!(ff_pkt->flags & FO_MTIMEONLY) 
-              && (statc.st_ctime != ff_pkt->statp.st_ctime)) {
-//   Jmsg(jcr, M_SAVED, 0, _("%s      st_ctime differs\n"), fname);
-      Dmsg1(dbglvl, "%s      st_ctime differs\n", fname);
-      stat = true;
-
-   } else if (statc.st_size != ff_pkt->statp.st_size) {
-//   Jmsg(jcr, M_SAVED, 0, _("%s      st_size differs\n"), fname);
-      Dmsg1(dbglvl, "%s      st_size differs\n", fname);
-      stat = true;
+      }
    }
-#endif
 
    /* 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 (jcr->getJobLevel() == L_FULL) {
       if (!stat) {               
          /* compute space saved with basefile */
          jcr->base_size += ff_pkt->statp.st_size;
@@ -402,8 +496,9 @@ bail_out:
 int accurate_cmd(JCR *jcr)
 {
    BSOCK *dir = jcr->dir_bsock;
-   int len;
+   int lstat_pos, chksum_pos;
    int32_t nb;
+   uint16_t delta_seq;
 
    if (job_canceled(jcr)) {
       return true;
@@ -419,13 +514,28 @@ int accurate_cmd(JCR *jcr)
 
    /*
     * buffer = sizeof(CurFile) + dirmsg
-    * dirmsg = fname + \0 + lstat
+    * dirmsg = fname + \0 + lstat + \0 + checksum + \0 + delta_seq + \0
     */
    /* get current files */
    while (dir->recv() >= 0) {
-      len = strlen(dir->msg) + 1;
-      if (len < dir->msglen) {
-         accurate_add_file(jcr, dir->msg, dir->msg + len);
+      lstat_pos = strlen(dir->msg) + 1;
+      if (lstat_pos < dir->msglen) {
+         chksum_pos = lstat_pos + strlen(dir->msg + lstat_pos) + 1;
+
+         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, 
+                           dir->msg,               /* Path */
+                           dir->msg + lstat_pos,   /* LStat */
+                           dir->msg + chksum_pos,  /* CheckSum */
+                           delta_seq);             /* Delta Sequence */
       }
    }