]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/patches/testing/project-accurate-backup.patch2
ebl compil fix
[bacula/bacula] / bacula / patches / testing / project-accurate-backup.patch2
index 01e0c8b8efff44debc033738a96ff7caa7c04620..e90ae5a0ba1892fcb3fb10fea028177384aedc98 100644 (file)
@@ -1,6 +1,6 @@
 Index: src/dird/fd_cmds.c
 ===================================================================
---- src/dird/fd_cmds.c (révision 6443)
+--- src/dird/fd_cmds.c (révision 6467)
 +++ src/dird/fd_cmds.c (copie de travail)
 @@ -50,7 +50,7 @@
  static char filesetcmd[]  = "fileset%s\n"; /* set full fileset */
@@ -11,7 +11,7 @@ Index: src/dird/fd_cmds.c
  static char runscript[]   = "Run OnSuccess=%u OnFailure=%u AbortOnError=%u When=%u Command=%s\n";
  static char runbeforenow[]= "RunBeforeNow\n";
  
-@@ -226,7 +226,7 @@
+@@ -226,13 +226,12 @@
     char ed1[50];
  
     stime = str_to_utime(jcr->stime);
@@ -20,24 +20,31 @@ Index: src/dird/fd_cmds.c
     while (bget_dirmsg(fd) >= 0) {  /* allow him to poll us to sync clocks */
        Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg);
     }
-@@ -240,24 +240,25 @@
+ }
+-
+ /*
+  * Send level command to FD.
+  * Used for backup jobs and estimate command.
+@@ -240,24 +239,26 @@
  bool send_level_command(JCR *jcr)
  {
     BSOCK   *fd = jcr->file_bsock;
 +   const char *accurate=jcr->job->accurate?"accurate_":"";
++   const char *not_accurate="";
     /*
      * Send Level command to File daemon
      */
     switch (jcr->JobLevel) {
     case L_BASE:
 -      fd->fsend(levelcmd, "base", " ", 0);
-+      fd->fsend(levelcmd, "", "base", " ", 0);
++      fd->fsend(levelcmd, not_accurate, "base", " ", 0);
        break;
     /* L_NONE is the console, sending something off to the FD */
     case L_NONE:
     case L_FULL:
 -      fd->fsend(levelcmd, "full", " ", 0);
-+      fd->fsend(levelcmd, "", "full", " ", 0);
++      fd->fsend(levelcmd, not_accurate, "full", " ", 0);
        break;
     case L_DIFFERENTIAL:
 -      fd->fsend(levelcmd, "differential", " ", 0);
@@ -52,20 +59,14 @@ Index: src/dird/fd_cmds.c
     case L_SINCE:
 Index: src/dird/backup.c
 ===================================================================
---- src/dird/backup.c  (révision 6443)
+--- src/dird/backup.c  (révision 6467)
 +++ src/dird/backup.c  (copie de travail)
-@@ -44,6 +44,7 @@
- #include "bacula.h"
- #include "dird.h"
- #include "ua.h"
-+#include "findlib/find.h"
- /* Commands sent to File daemon */
- static char backupcmd[] = "backup\n";
-@@ -96,7 +97,93 @@
-    return true;
+@@ -97,6 +97,65 @@
  }
  
+ /*
++ * Foreach files in currrent list, send "/path/fname\0LStat" to FD
++ */
 +static int accurate_list_handler(void *ctx, int num_fields, char **row)
 +{
 +   JCR *jcr = (JCR *)ctx;
@@ -74,81 +75,51 @@ Index: src/dird/backup.c
 +      return 1;
 +   }
 +   
-+   if (row[0] > 0) {          /* discard when file_index == 0 */
-+      jcr->file_bsock->fsend("%s%s%c%s", row[1], row[2], 0, row[3]); 
++   if (row[2] > 0) {            /* discard when file_index == 0 */
++      jcr->file_bsock->fsend("%s%s%c%s", row[0], row[1], 0, row[4]); 
 +   }
 +   return 0;
 +}
 +
-+/* Full : do nothing
-+ * Differential : get the last full id
-+ * Incremental : get the last full + last diff + last incr(s) ids
-+ *
-+ * TODO: look and merge from ua_restore.c
++/*
++ * Send current file list to FD
++ *    DIR -> FD : accurate files=xxxx
++ *    DIR -> FD : /path/to/file\0Lstat
++ *    DIR -> FD : /path/to/dir/\0Lstat
++ *    ...
++ *    DIR -> FD : EOD
 + */
-+bool db_accurate_get_jobids(JCR *jcr, POOLMEM *jobids)
-+{
-+   pm_strcpy(jobids, "42");
-+   return 1;
-+}
-+
 +bool send_accurate_current_files(JCR *jcr)
 +{
-+   char buf[MAXSTRING];
-+   char ed1[50], ed2[50];
++   POOL_MEM buf;
 +
-+   if (jcr->accurate == false || job_canceled(jcr) || jcr->JobLevel == L_FULL) {
++   if (jcr->accurate==false || job_canceled(jcr) || jcr->JobLevel==L_FULL) {
 +      return true;
 +   }
-+
 +   POOLMEM *jobids = get_pool_memory(PM_FNAME);
-+   db_accurate_get_jobids(jcr, jobids);
-+
-+   bsnprintf(buf, sizeof(buf),
-+            "CREATE TEMPORARY TABLE btemp2%s AS ( "
-+            "SELECT max(FileId) as FileId, PathId, FilenameId "
-+            "FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (%s)) AS F "
-+            "GROUP BY PathId, FilenameId ) ",
-+            edit_uint64(jcr->JobId, ed1),
-+            jobids);
-+   db_sql_query(jcr->db, buf, NULL, NULL);
-+
-+   bsnprintf(buf, sizeof(buf), "SELECT count(1) FROM btemp2%s",ed1);
-+   db_sql_query(jcr->db, buf, NULL, NULL);  // TODO: compter le nombre de rows 
-+
-+   jcr->file_bsock->fsend("accurate files=%s\n", edit_uint64(337969*2, ed2)); 
-+
-+   bsnprintf(buf, sizeof(buf),
-+           "SELECT File.FileIndex, Path.Path, Filename.Name, File.LStat "
-+             "FROM btemp2%s JOIN Path USING (PathId) JOIN Filename USING (FilenameId) "
-+             "JOIN File USING (FileId) "
-+            "WHERE File.FileIndex > 0",
-+           ed1, jobids);
-+   db_sql_query(jcr->db, buf, accurate_list_handler, (void *)jcr);
-+
-+   bsnprintf(buf, sizeof(buf), "DROP TABLE btemp2%s", ed1);
-+   free_pool_memory(jobids);
++   db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, jobids);
 +
- /*
-+ CREATE TEMPORARY TABLE btemp2 AS (
-+  SELECT max(FileId) as FileId, PathId, FilenameId 
-+    FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (39867,40341)) AS F
-+   GROUP BY PathId, FilenameId )
-+
-+  SELECT File.FileIndex, Path.Path, Filename.Name, File.LStat
-+    FROM btemp2 JOIN Path USING (PathId) JOIN Filename USING (FilenameId)
-+                JOIN File USING (FileId)
-+   WHERE File.FileIndex > 0
-+
-+ DROP TABLE btemp2
-+*/
-+/*
-+SELECT DISTINCT ON (PathId, FilenameId) FileIndex, Path, Name, LStat
-+  FROM File JOIN Filename USING (FilenameId) JOIN Path USING (PathId) WHERE JobId IN (40341)
-+ ORDER BY PathId, FilenameId, JobId DESC
-+*/
++   if (*jobids == 0) {
++      free_pool_memory(jobids);
++      Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
++      return false;
++   }
++   Jmsg(jcr, M_INFO, 0, _("Sending Accurate information.\n"));
++
++   /* to be able to allocate the right size for htable */
++   POOLMEM *nb = get_pool_memory(PM_FNAME);
++   Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)",jobids);
++   db_sql_query(jcr->db, buf.c_str(), db_get_int_handler, nb);
++   jcr->file_bsock->fsend("accurate files=%s\n", nb); 
++
++   db_get_file_list(jcr, jcr->db, jobids, accurate_list_handler, (void *)jcr);
++
++   free_pool_memory(jobids);
++   free_pool_memory(nb);
 +
 +   jcr->file_bsock->signal(BNET_EOD);
++   /* TODO: use response() ? */
++
 +   return true;
 +}
 +
@@ -156,13 +127,13 @@ Index: src/dird/backup.c
   * Do a backup of the specified FileSet
   *
   *  Returns:  false on failure
-@@ -225,6 +312,14 @@
+@@ -225,6 +284,14 @@
        Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
     }
  
 +   /*
-+    * If backup is in accurate mode, FD will send the list of
-+    * all files.
++    * If backup is in accurate mode, we send the list of
++    * all files to FD.
 +    */
 +   if (!send_accurate_current_files(jcr)) {
 +      goto bail_out;
@@ -171,81 +142,136 @@ Index: src/dird/backup.c
     /* Send backup command */
     fd->fsend(backupcmd);
     if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
-@@ -234,6 +329,7 @@
-    /* Pickup Job termination data */
-    stat = wait_for_job_termination(jcr);
-    db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
-+
-    if (stat == JS_Terminated) {
-       backup_cleanup(jcr, stat);
-       return true;
-Index: src/dird/inc_conf.c
+@@ -475,6 +542,7 @@
+ "  Software Compression:   %s\n"
+ "  VSS:                    %s\n"
+ "  Encryption:             %s\n"
++"  Accurate:               %s\n"
+ "  Volume name(s):         %s\n"
+ "  Volume Session Id:      %d\n"
+ "  Volume Session Time:    %d\n"
+@@ -506,8 +574,9 @@
+         edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
+         kbps,
+         compress,
+-        jcr->VSS?"yes":"no",
+-        jcr->Encrypt?"yes":"no",
++        jcr->VSS?_("yes"):_("no"),
++        jcr->Encrypt?_("yes"):_("no"),
++        jcr->accurate?_("yes"):_("no"),
+         jcr->VolumeName,
+         jcr->VolSessionId,
+         jcr->VolSessionTime,
+Index: src/dird/ua_restore.c
 ===================================================================
---- src/dird/inc_conf.c        (révision 6443)
-+++ src/dird/inc_conf.c        (copie de travail)
-@@ -94,6 +94,7 @@
-  * Items that are valid in an Options resource
-  */
- static RES_ITEM options_items[] = {
-+   {"accurate",        store_opts,    {0},     0, 0, 0},
-    {"compression",     store_opts,    {0},     0, 0, 0},
-    {"signature",       store_opts,    {0},     0, 0, 0},
-    {"verify",          store_opts,    {0},     0, 0, 0},
-@@ -153,7 +154,8 @@
-    INC_KW_NOATIME,
-    INC_KW_ENHANCEDWILD,
-    INC_KW_CHKCHANGES,
--   INC_KW_STRIPPATH
-+   INC_KW_STRIPPATH,
-+   INC_KW_ACCURATE
- };
+--- src/dird/ua_restore.c      (révision 6467)
++++ src/dird/ua_restore.c      (copie de travail)
+@@ -1005,7 +1005,6 @@
+     * For display purposes, the same JobId, with different volumes may
+     * appear more than once, however, we only insert it once.
+     */
+-   int items = 0;
+    p = rx->JobIds;
+    tree.FileEstimate = 0;
+    if (get_next_jobid_from_list(&p, &JobId) > 0) {
+@@ -1020,23 +1019,12 @@
+          tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
+       }
+    }
+-   for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
+-      char ed1[50];
  
- /*
-@@ -163,6 +165,7 @@
-  *   options given above.
-  */
- static struct s_kw FS_option_kw[] = {
-+   {"accurate",    INC_KW_ACCURATE},
-    {"compression", INC_KW_COMPRESSION},
-    {"signature",   INC_KW_DIGEST},
-    {"encryption",  INC_KW_ENCRYPTION},
-@@ -251,6 +254,8 @@
-    {"no",       INC_KW_ENHANCEDWILD,  "0"},
-    {"yes",      INC_KW_CHKCHANGES,    "c"},
-    {"no",       INC_KW_CHKCHANGES,    "0"},
-+   {"yes",      INC_KW_ACCURATE,      "C"},
-+   {"no",       INC_KW_ACCURATE,      "0"},
-    {NULL,       0,                      0}
- };
+-      if (JobId == last_JobId) {
+-         continue;                    /* eliminate duplicate JobIds */
+-      }
+-      last_JobId = JobId;
+-      ua->info_msg(_("\nBuilding directory tree for JobId %s ...  "), 
+-         edit_int64(JobId, ed1));
+-      items++;
+-      /*
+-       * Find files for this JobId and insert them in the tree
+-       */
+-      Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
+-      if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
+-         ua->error_msg("%s", db_strerror(ua->db));
+-      }
++   ua->info_msg(_("\nBuilding directory tree for JobId(s) %s ...  "),
++                rx->JobIds);
++
++   if (!db_get_file_list(ua->jcr, ua->db, rx->JobIds, insert_tree_handler, (void *)&tree)) {
++      ua->error_msg("%s", db_strerror(ua->db));
+    }
+    if (tree.FileCount == 0) {
+       ua->send_msg(_("\nThere were no files inserted into the tree, so file selection\n"
+@@ -1055,26 +1043,13 @@
+       }
+    } else {
+       char ec1[50];
+-      if (items==1) {
+-         if (tree.all) {
+-            ua->info_msg(_("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
+-              edit_uint64_with_commas(tree.FileCount, ec1));
+-         }
+-         else {
+-            ua->info_msg(_("\n1 Job, %s files inserted into the tree.\n"),
+-              edit_uint64_with_commas(tree.FileCount, ec1));
+-         }
++      if (tree.all) {
++         ua->info_msg(_("\n%s files inserted into the tree and marked for extraction.\n"),
++                      edit_uint64_with_commas(tree.FileCount, ec1));
++      } else {
++         ua->info_msg(_("\n%s files inserted into the tree.\n"),
++                      edit_uint64_with_commas(tree.FileCount, ec1));
+       }
+-      else {
+-         if (tree.all) {
+-            ua->info_msg(_("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
+-              items, edit_uint64_with_commas(tree.FileCount, ec1));
+-         }
+-         else {
+-            ua->info_msg(_("\n%d Jobs, %s files inserted into the tree.\n"),
+-              items, edit_uint64_with_commas(tree.FileCount, ec1));
+-         }
+-      }
  
+       if (find_arg(ua, NT_("done")) < 0) {
+          /* Let the user interact in selecting which files to restore */
 Index: src/filed/backup.c
 ===================================================================
---- src/filed/backup.c (révision 6443)
+--- src/filed/backup.c (révision 6467)
 +++ src/filed/backup.c (copie de travail)
-@@ -48,8 +48,220 @@
+@@ -37,6 +37,7 @@
+ #include "bacula.h"
+ #include "filed.h"
++#include "lib/htable.h"
+ /* Forward referenced functions */
+ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
+@@ -48,8 +49,231 @@
  static bool crypto_session_start(JCR *jcr);
  static void crypto_session_end(JCR *jcr);
  static bool crypto_session_send(JCR *jcr, BSOCK *sd);
 +static bool encode_and_send_deleted_file(JCR *jcr, char *fname);
  
-+#include "lib/htable.c"
 +typedef struct CurFile {
 +   char *fname;
 +   char *lstat;
 +   hlink link;
 +} CurFile;
++
++#define accurate_mark_file_as_seen(elt) ((elt)->lstat[0] = 0)
++#define accurate_file_has_been_seen(elt) ((elt)->lstat[0] == 0)
 +
  /*
 + * This function is called for each file seen in fileset.
-+ * 
-+ * If the file is skipped (saved=false), we will check if this
-+ * file have been backuped before. If not, we decide to backup it.
-+ *
-+ * If the file have saved=true, we mark it as seen
++ * We check in file_list hash if fname have been backuped
++ * the last time. After we can compare Lstat field. When
++ * a file have been seen, we put '\0' in LStat field.
 + * 
 + */
-+/* TODO: tweak verify code to use the same function */
-+bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt, bool saved)
++/* TODO: tweak verify code to use the same function ?? */
++bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
 +{
 +   char *p;
 +   int stat=false;
@@ -260,21 +286,19 @@ Index: src/filed/backup.c
 +      return true;
 +   }
 +
-+   if (ff_pkt->type == FT_DIREND || ff_pkt->type == FT_REPARSE || ff_pkt->type == FT_DIRNOCHG) {
++   if (S_ISDIR(ff_pkt->statp.st_mode)) {
 +      fname = ff_pkt->link;
 +   } 
 +
 +   elt = (CurFile *) jcr->file_list->lookup(fname);
 +
 +   if (!elt) {
-+      // TODO: we must backup it !
-+      Dmsg1(1, "accurate %s = yes (not found)\n", fname);
++      Dmsg1(500, "accurate %s = yes (not found)\n", fname);
 +      return true;
 +   }
 +
-+   if (saved || *elt->lstat == '\0') {
-+      Dmsg1(1, "accurate %s = no (already seen)\n", fname);
-+      *elt->lstat = '\0';
++   if (accurate_file_has_been_seen(elt)) {
++      Dmsg1(500, "accurate %s = no (already seen)\n", fname);
 +      return false;
 +   }
 +
@@ -285,102 +309,105 @@ Index: src/filed/backup.c
 +      char ed1[30], ed2[30];
 +      switch (*p) {
 +      case 'i':                /* compare INODEs */
-+       if (statc.st_ino != ff_pkt->statp.st_ino) {
-+          Jmsg(jcr, M_INFO, 0, _("      st_ino   differ. Cat: %s File: %s\n"),
-+               edit_uint64((uint64_t)statc.st_ino, ed1),
-+               edit_uint64((uint64_t)ff_pkt->statp.st_ino, ed2));
-+          stat = true;
-+       }
-+       break;
++         if (statc.st_ino != ff_pkt->statp.st_ino) {
++            Jmsg(jcr, M_SAVED, 0, _("%s      st_ino   differ. Cat: %s File: %s\n"), fname,
++                 edit_uint64((uint64_t)statc.st_ino, ed1),
++                 edit_uint64((uint64_t)ff_pkt->statp.st_ino, ed2));
++            stat = true;
++         }
++         break;
 +      case 'p':                /* permissions bits */
-+       if (statc.st_mode != ff_pkt->statp.st_mode) {
-+          Jmsg(jcr, M_INFO, 0, _("      st_mode  differ. Cat: %x File: %x\n"),
-+               (uint32_t)statc.st_mode, (uint32_t)ff_pkt->statp.st_mode);
-+          stat = true;
-+       }
-+       break;
-+      case 'n':                /* number of links */
-+       if (statc.st_nlink != ff_pkt->statp.st_nlink) {
-+          Jmsg(jcr, M_INFO, 0, _("      st_nlink differ. Cat: %d File: %d\n"),
-+               (uint32_t)statc.st_nlink, (uint32_t)ff_pkt->statp.st_nlink);
-+          stat = true;
-+       }
-+       break;
++         if (statc.st_mode != ff_pkt->statp.st_mode) {
++            Jmsg(jcr, M_SAVED, 0, _("%s      st_mode  differ. Cat: %x File: %x\n"), fname,
++                 (uint32_t)statc.st_mode, (uint32_t)ff_pkt->statp.st_mode);
++            stat = true;
++         }
++         break;
++//      case 'n':                /* number of links */
++//         if (statc.st_nlink != ff_pkt->statp.st_nlink) {
++//            Jmsg(jcr, M_SAVED, 0, _("%s      st_nlink differ. Cat: %d File: %d\n"), fname,
++//                 (uint32_t)statc.st_nlink, (uint32_t)ff_pkt->statp.st_nlink);
++//            stat = true;
++//         }
++//         break;
 +      case 'u':                /* user id */
-+       if (statc.st_uid != ff_pkt->statp.st_uid) {
-+          Jmsg(jcr, M_INFO, 0, _("      st_uid   differ. Cat: %u File: %u\n"),
-+               (uint32_t)statc.st_uid, (uint32_t)ff_pkt->statp.st_uid);
-+          stat = true;
-+       }
-+       break;
++         if (statc.st_uid != ff_pkt->statp.st_uid) {
++            Jmsg(jcr, M_SAVED, 0, _("%s      st_uid   differ. Cat: %u File: %u\n"), fname,
++                 (uint32_t)statc.st_uid, (uint32_t)ff_pkt->statp.st_uid);
++            stat = true;
++         }
++         break;
 +      case 'g':                /* group id */
-+       if (statc.st_gid != ff_pkt->statp.st_gid) {
-+          Jmsg(jcr, M_INFO, 0, _("      st_gid   differ. Cat: %u File: %u\n"),
-+               (uint32_t)statc.st_gid, (uint32_t)ff_pkt->statp.st_gid);
-+          stat = true;
-+       }
-+       break;
++         if (statc.st_gid != ff_pkt->statp.st_gid) {
++            Jmsg(jcr, M_SAVED, 0, _("%s      st_gid   differ. Cat: %u File: %u\n"), fname,
++                 (uint32_t)statc.st_gid, (uint32_t)ff_pkt->statp.st_gid);
++            stat = true;
++         }
++         break;
 +      case 's':                /* size */
-+       if (statc.st_size != ff_pkt->statp.st_size) {
-+          Jmsg(jcr, M_INFO, 0, _("      st_size  differ. Cat: %s File: %s\n"),
-+               edit_uint64((uint64_t)statc.st_size, ed1),
-+               edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
-+          stat = true;
-+       }
-+       break;
-+      case 'a':                /* access time */
-+       if (statc.st_atime != ff_pkt->statp.st_atime) {
-+          Jmsg(jcr, M_INFO, 0, _("      st_atime differs\n"));
-+          stat = true;
-+       }
-+       break;
++         if (statc.st_size != ff_pkt->statp.st_size) {
++            Jmsg(jcr, M_SAVED, 0, _("%s      st_size  differ. Cat: %s File: %s\n"), fname,
++                 edit_uint64((uint64_t)statc.st_size, ed1),
++                 edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
++            stat = true;
++         }
++         break;
++//      case 'a':                /* access time */
++//         if (statc.st_atime != ff_pkt->statp.st_atime) {
++//            Jmsg(jcr, M_SAVED, 0, _("%s      st_atime differs\n"), fname);
++//            stat = true;
++//         }
++//         break;
 +      case 'm':
-+       if (statc.st_mtime != ff_pkt->statp.st_mtime) {
-+          Jmsg(jcr, M_INFO, 0, _("      st_mtime differs\n"));
-+          stat = true;
-+       }
-+       break;
++         if (statc.st_mtime != ff_pkt->statp.st_mtime) {
++            Jmsg(jcr, M_SAVED, 0, _("%s      st_mtime differs\n"), fname);
++            stat = true;
++         }
++         break;
 +      case 'c':                /* ctime */
-+       if (statc.st_ctime != ff_pkt->statp.st_ctime) {
-+          Jmsg(jcr, M_INFO, 0, _("      st_ctime differs\n"));
-+          stat = true;
-+       }
-+       break;
++         if (statc.st_ctime != ff_pkt->statp.st_ctime) {
++            Jmsg(jcr, M_SAVED, 0, _("%s      st_ctime differs\n"), fname);
++            stat = true;
++         }
++         break;
 +      case 'd':                /* file size decrease */
-+       if (statc.st_size > ff_pkt->statp.st_size) {
-+          Jmsg(jcr, M_INFO, 0, _("      st_size  decrease. Cat: %s File: %s\n"),
-+               edit_uint64((uint64_t)statc.st_size, ed1),
-+               edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
-+          stat = true;
-+       }
-+       break;
++         if (statc.st_size > ff_pkt->statp.st_size) {
++            Jmsg(jcr, M_SAVED, 0, _("%s      st_size  decrease. Cat: %s File: %s\n"), fname,
++                 edit_uint64((uint64_t)statc.st_size, ed1),
++                 edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
++            stat = true;
++         }
++         break;
 +      case '5':                /* compare MD5 */
-+       Dmsg1(500, "set Do_MD5 for %s\n", ff_pkt->fname);
-+//     *do_Digest = CRYPTO_DIGEST_MD5;
-+       break;
++         Dmsg1(500, "set Do_MD5 for %s\n", ff_pkt->fname);
++//       *do_Digest = CRYPTO_DIGEST_MD5;
++         break;
 +      case '1':                 /* compare SHA1 */
-+//     *do_Digest = CRYPTO_DIGEST_SHA1;
-+       break;
++//       *do_Digest = CRYPTO_DIGEST_SHA1;
++         break;
 +      case ':':
 +      case 'V':
 +      default:
-+       break;
++         break;
 +      }
 +   }
-+   *elt->lstat = '\0';                /* mark it as seen */
-+   Dmsg2(1, "accurate %s = %i\n", fname, stat);
++   accurate_mark_file_as_seen(elt);
++   Dmsg2(500, "accurate %s = %i\n", fname, stat);
 +   return stat;
 +}
 +
-+static int deb=0;
-+int accurate_get_current_file_list_cmd(JCR *jcr)
++/* 
++ * This function doesn't work very well with smartalloc
++ * TODO: use bigbuffer from htable
++ */
++int accurate_cmd(JCR *jcr)
 +{
 +   BSOCK *dir = jcr->dir_bsock;
 +   int len;
 +   uint64_t nb;
 +   CurFile *elt=NULL;
 +
-+   if (jcr->accurate == false || job_canceled(jcr) || jcr->JobLevel == L_FULL) {
++   if (jcr->accurate==false || job_canceled(jcr) || jcr->JobLevel==L_FULL) {
 +      return true;
 +   }
 +
@@ -388,54 +415,64 @@ Index: src/filed/backup.c
 +      dir->fsend(_("2991 Bad accurate command\n"));
 +      return false;
 +   }
-+   
++
 +   jcr->file_list = (htable *)malloc(sizeof(htable));
 +   jcr->file_list->init(elt, &elt->link, nb);
 +
++   /*
++    * buffer = sizeof(CurFile) + dirmsg
++    * dirmsg = fname + lstat
++    */
 +   /* get current files */
 +   while (dir->recv() >= 0) {
 +      len = strlen(dir->msg);
 +      if ((len+1) < dir->msglen) {
-+       elt = (CurFile *)malloc(sizeof(CurFile));
-+       elt->fname  = (char *) malloc(dir->msglen+1);
-+       memcpy(elt->fname, dir->msg, dir->msglen);
-+       elt->fname[dir->msglen]='\0';
-+       elt->lstat = elt->fname + len + 1;
-+       if ((deb % 1000) == 1) {
-+          Dmsg1(1, "deb=%i\n", deb);
-+       }
-+       if ((deb % 5000) == 1) {
-+          jcr->file_list->stats();
-+       }
-+       Dmsg2(100, "hash[%s]=%s\n", elt->fname, elt->lstat);
-+       jcr->file_list->insert(elt->fname, elt); 
-+       deb++;
++//       elt = (CurFile *)malloc(sizeof(CurFile));
++//       elt->fname  = (char *) malloc(dir->msglen+1);
++
++         /* we store CurFile, fname and lstat in the same chunk */
++         elt = (CurFile *)malloc(sizeof(CurFile)+dir->msglen+1);
++         elt->fname  = (char *) elt+sizeof(CurFile);
++         memcpy(elt->fname, dir->msg, dir->msglen);
++         elt->fname[dir->msglen]='\0';
++         elt->lstat = elt->fname + len + 1;
++         jcr->file_list->insert(elt->fname, elt); 
++         Dmsg2(500, "add fname=%s lstat=%s\n", elt->fname, elt->lstat);
 +      }
 +   }
-+  
-+   jcr->file_list->stats();
-+//   dir->fsend("2000 OK accurate\n");
 +
++//   jcr->file_list->stats();
++   /* TODO: send a EOM ?
++   dir->fsend("2000 OK accurate\n");
++    */
 +   return true;
 +}
 +
 +bool accurate_send_deleted_list(JCR *jcr)
 +{
 +   if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
-+      return true;
++      goto bailout;
++   }
++
++   if (jcr->file_list == NULL) {
++      goto bailout;
 +   }
 +
 +   CurFile *elt;
 +   foreach_htable (elt, jcr->file_list) {
-+      Dmsg3(100, "elt = 0x%x fname=%s lstat=%s\n", elt, elt->fname, elt->lstat);
-+      if (*elt->lstat != '\0') {
-+       encode_and_send_deleted_file(jcr, elt->fname);
++      if (accurate_file_has_been_seen(elt)) { /* already seen */
++         Dmsg2(500, "deleted fname=%s lstat=%s\n", elt->fname, elt->lstat);
++         encode_and_send_deleted_file(jcr, elt->fname);
 +      }
-+      free(elt->fname);
++//      free(elt->fname);
++   }
++bailout:
++   /* TODO: clean htable when this function is not reached ? */
++   if (jcr->file_list) {
++      jcr->file_list->destroy();
++      free(jcr->file_list);
++      jcr->file_list = NULL;
 +   }
-+   jcr->file_list->destroy(); /* TODO: clean htable when this function is not reached ? */
-+   free(jcr->file_list);
-+   jcr->file_list = NULL;
 +   return true;
 +}
 +
@@ -443,56 +480,41 @@ Index: src/filed/backup.c
   * Find all the requested files and send them
   * to the Storage daemon.
   *
-@@ -66,7 +278,6 @@
-    BSOCK *sd;
-    bool ok = true;
-    // TODO landonf: Allow user to specify encryption algorithm
+@@ -100,7 +324,7 @@
+     */
+    jcr->compress_buf_size = jcr->buf_size + ((jcr->buf_size+999) / 1000) + 30;
+    jcr->compress_buf = get_memory(jcr->compress_buf_size);
 -
-    sd = jcr->store_bsock;
++   
+ #ifdef HAVE_LIBZ
+    z_stream *pZlibStream = (z_stream*)malloc(sizeof(z_stream));  
+    if (pZlibStream) {
+@@ -121,10 +345,13 @@
+       return false;
+    }
  
-    set_jcr_job_status(jcr, JS_Running);
-@@ -134,7 +345,10 @@
-       ok = false;                     /* error */
+-   Dmsg1(300, "set_find_options ff=%p\n", jcr->ff);
+    set_find_options((FF_PKT *)jcr->ff, jcr->incremental, jcr->mtime);
+-   Dmsg0(300, "start find files\n");
++   /* in accurate mode, we overwrite the find_one check function */
++   if (jcr->accurate) {
++      set_find_changed_function((FF_PKT *)jcr->ff, accurate_check_file);
++   } 
++   
+    start_heartbeat_monitor(jcr);
+    jcr->acl_text = get_pool_memory(PM_MESSAGE);
+@@ -135,6 +362,8 @@
        set_jcr_job_status(jcr, JS_ErrorTerminated);
     }
-+   Dmsg1(1, "jcr->accurate == %i\n", jcr->accurate);
  
 +   accurate_send_deleted_list(jcr);              /* send deleted list to SD  */
 +
     free_pool_memory(jcr->acl_text);
  
     stop_heartbeat_monitor(jcr);
-@@ -354,9 +568,19 @@
-    }
-    case FT_DIRNOCHG:
-    case FT_NOCHG:
-+      /* TODO: in accurate mode, we have to change NOCHG attribute to FT_REG... */
-+//      if (!accurate_check_file(jcr, ff_pkt, false)) {
-+//     Jmsg(jcr, M_SKIPPED, 1, _("     Unchanged file skipped: %s\n"), ff_pkt->fname);
-+//     return 1;
-+//      }
-       Jmsg(jcr, M_SKIPPED, 1, _("     Unchanged file skipped: %s\n"), ff_pkt->fname);
-       return 1;
-    case FT_ISARCH:
-+      /* TODO: in accurate mode, we have to change NOCHG attribute to FT_REG... */
-+//      if (!accurate_check_file(jcr, ff_pkt, false)) { 
-+//     Jmsg(jcr, M_NOTSAVED, 0, _("     Archive file not saved: %s\n"), ff_pkt->fname);
-+//     return 1;
-+//      }
-       Jmsg(jcr, M_NOTSAVED, 0, _("     Archive file not saved: %s\n"), ff_pkt->fname);
-       return 1;
-    case FT_NOOPEN: {
-@@ -1118,6 +1342,9 @@
-    }
-    unstrip_path(ff_pkt);
-+   /* list backuped files */
-+   accurate_check_file(jcr, ff_pkt, true);
-+
-    Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
-    if (!stat) {
-       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-@@ -1128,6 +1355,58 @@
+@@ -1128,6 +1357,57 @@
     return true;
  }
  
@@ -532,11 +554,10 @@ Index: src/filed/backup.c
 +    * For a directory, link is the same as fname, but with trailing
 +    * slash. For a linked file, link is the link.
 +    */
-+   stat = sd->fsend("%ld %d %s%c%s%c%s%c%s%c", 
-+                  0 /* FileIndex */,
-+                  FT_NOSTAT /* FileType */,
-+                  fname /* FileName */, 
-+                  0, attribs, 0, 0, 0, attribsEx, 0);
++   stat = sd->fsend("0 %d %s%c%s%c%s%c%s%c", 
++                    FT_NOSTAT /* FileType */,
++                    fname /* FileName */, 
++                    0, attribs, 0, 0, 0, attribsEx, 0);
 +
 +   Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
 +   if (!stat) {
@@ -553,13 +574,13 @@ Index: src/filed/backup.c
   */
 Index: src/filed/job.c
 ===================================================================
---- src/filed/job.c    (révision 6443)
+--- src/filed/job.c    (révision 6467)
 +++ src/filed/job.c    (copie de travail)
 @@ -49,6 +49,7 @@
  /* Imported functions */
  extern int status_cmd(JCR *jcr);
  extern int qstatus_cmd(JCR *jcr);
-+extern int accurate_get_current_file_list_cmd(JCR *jcr);
++extern int accurate_cmd(JCR *jcr);
  
  /* Forward referenced functions */
  static int backup_cmd(JCR *jcr);
@@ -567,21 +588,11 @@ Index: src/filed/job.c
     {"RunBeforeJob", runbefore_cmd, 0},
     {"RunAfterJob",  runafter_cmd,  0},
     {"Run",          runscript_cmd, 0},
-+   {"accurate",     accurate_get_current_file_list_cmd, 0},
++   {"accurate",     accurate_cmd, 0},
     {NULL,       NULL}                  /* list terminator */
  };
  
-@@ -1087,6 +1089,9 @@
-       case 'c':
-          fo->flags |= FO_CHKCHANGES;
-          break;
-+      case 'C':
-+         fo->flags |= FO_ACCURATE;
-+         break;
-       default:
-          Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
-          break;
-@@ -1195,6 +1200,9 @@
+@@ -1195,6 +1197,9 @@
  
     level = get_memory(dir->msglen+1);
     Dmsg1(110, "level_cmd: %s", dir->msg);
@@ -591,7 +602,7 @@ Index: src/filed/job.c
     if (sscanf(dir->msg, "level = %s ", level) != 1) {
        goto bail_out;
     }
-@@ -1204,14 +1212,14 @@
+@@ -1204,14 +1209,14 @@
     /* Full backup requested? */
     } else if (strcmp(level, "full") == 0) {
        jcr->JobLevel = L_FULL;
@@ -611,7 +622,7 @@ Index: src/filed/job.c
      *   to agree with our clock.
 Index: src/filed/restore.c
 ===================================================================
---- src/filed/restore.c        (révision 6443)
+--- src/filed/restore.c        (révision 6467)
 +++ src/filed/restore.c        (copie de travail)
 @@ -320,6 +320,11 @@
              bclose(&rctx.bfd);
@@ -625,9 +636,178 @@ Index: src/filed/restore.c
           /*
            * Unpack attributes and do sanity check them
            */
+Index: src/cats/protos.h
+===================================================================
+--- src/cats/protos.h  (révision 6467)
++++ src/cats/protos.h  (copie de travail)
+@@ -102,6 +102,9 @@
+ int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cdbr);
+ int db_get_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr);
+ bool db_get_query_dbids(JCR *jcr, B_DB *mdb, POOL_MEM &query, dbid_list &ids);
++bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids, DB_RESULT_HANDLER *result_handler, void *ctx);
++bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM *jobids);
++int db_get_int_handler(void *ctx, int num_fields, char **row);
+ /* sql_list.c */
+Index: src/cats/sql_get.c
+===================================================================
+--- src/cats/sql_get.c (révision 6467)
++++ src/cats/sql_get.c (copie de travail)
+@@ -898,8 +898,6 @@
+    return ok;
+ }
+-
+-
+ /* Get Media Record
+  *
+  * Returns: false: on failure
+@@ -1018,5 +1016,141 @@
+    return ok;
+ }
++/*
++ * Find the last "accurate" backup state (that can take deleted files in account)
++ * 1) Get all files with jobid in list (F subquery) 
++ * 2) Take only the last version of each file (Temp subquery) => accurate list is ok
++ * 3) Join the result to file table to get fileindex, jobid and lstat information
++ *
++ * TODO: On postgresql, this is done with
++SELECT DISTINCT ON (PathId, FilenameId) FileIndex, Path, Name, LStat
++  FROM File JOIN Filename USING (FilenameId) JOIN Path USING (PathId) WHERE JobId IN (40341)
++ ORDER BY PathId, FilenameId, JobId DESC
++ */
++bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids, 
++                      DB_RESULT_HANDLER *result_handler, void *ctx)
++{
++   if (!*jobids) {
++      db_lock(mdb);
++      Mmsg(mdb->errmsg, _("ERR=JobIds are empty\n"));
++      db_unlock(mdb);
++      return false;
++   }
++   POOL_MEM buf (PM_MESSAGE);
++   
++   Mmsg(buf,
++ "SELECT Path.Path, Filename.Name, File.FileIndex, File.JobId, File.LStat "
++ "FROM ( "
++  "SELECT max(FileId) as FileId, PathId, FilenameId "
++    "FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (%s)) AS F "
++   "GROUP BY PathId, FilenameId "
++  ") AS Temp "
++ "JOIN Filename ON (Filename.FilenameId = Temp.FilenameId) "
++ "JOIN Path ON (Path.PathId = Temp.PathId) "
++ "JOIN File ON (File.FileId = Temp.FileId) "
++ "WHERE File.FileIndex > 0 ",
++             jobids);
++
++   return db_sql_query(mdb, buf.c_str(), result_handler, ctx);
++}
++
++
++/* Full : do nothing
++ * Differential : get the last full id
++ * Incremental : get the last full + last diff + last incr(s) ids
++ *
++ * TODO: look and merge from ua_restore.c
++ */
++bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, 
++                            JOB_DBR *jr, POOLMEM *jobids)
++{
++   char clientid[50], jobid[50], filesetid[50];
++   char date[MAX_TIME_LENGTH];
++
++   POOL_MEM query (PM_FNAME);
++   bstrutime(date, sizeof(date),  time(NULL) + 1);
++   jobids[0]='\0';
++
++   /* First, find the last good Full backup for this job/client/fileset */
++   Mmsg(query, 
++"CREATE TEMPORARY TABLE btemp3%s AS "
++ "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
++   "FROM Job JOIN FileSet USING (FileSetId) "
++  "WHERE ClientId = %s "
++    "AND Level='F' AND JobStatus='T' AND Type='B' "
++    "AND StartTime<'%s' "
++    "AND FileSet.FileSet=(SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
++  "ORDER BY Job.JobTDate DESC LIMIT 1",
++        edit_uint64(jcr->JobId, jobid),
++        edit_uint64(jr->ClientId, clientid),
++        date,
++        edit_uint64(jr->FileSetId, filesetid));
++
++   if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
++      return false;
++   }
++
++   if (jr->JobLevel == L_INCREMENTAL) {
++
++      /* Now, find the last differential backup after the last full */
++      Mmsg(query, 
++"INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
++ "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
++   "FROM Job JOIN FileSet USING (FileSetId) "
++  "WHERE ClientId = %s "
++    "AND Level='D' AND JobStatus='T' AND Type='B' "
++    "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
++    "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
++  "ORDER BY Job.JobTDate DESC LIMIT 1 ",
++           jobid,
++           clientid,
++           jobid,
++           filesetid);
++
++      db_sql_query(mdb, query.c_str(), NULL, NULL);
++
++      /* We just have to take all incremental after the last Full/Diff */
++      Mmsg(query, 
++"INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
++ "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
++   "FROM Job JOIN FileSet USING (FileSetId) "
++  "WHERE ClientId = %s "
++    "AND Level='I' AND JobStatus='T' AND Type='B' "
++    "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
++    "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
++  "ORDER BY Job.JobTDate DESC ",
++           jobid,
++           clientid,
++           jobid,
++           filesetid);
++      db_sql_query(mdb, query.c_str(), NULL, NULL);
++   }
++
++   /* build a jobid list ie: 1,2,3,4 */
++   Mmsg(query, "SELECT JobId FROM btemp3%s", jobid);
++   db_sql_query(mdb, query.c_str(), db_get_int_handler, jobids);
++   Dmsg1(1, "db_accurate_get_jobids=%s\n", jobids);
++
++   Mmsg(query, "DROP TABLE btemp3%s", jobid);
++   db_sql_query(mdb, query.c_str(), NULL, NULL);
++
++   return true;
++}
++
++/*
++ * Use to build a string of int list from a query. "10,20,30"
++ */
++int db_get_int_handler(void *ctx, int num_fields, char **row)
++{
++   POOLMEM *ret = (POOLMEM *)ctx;
++   if (num_fields == 1) {
++      if (ret[0]) {
++         pm_strcat(ret, ",");
++      }
++      pm_strcat(ret, row[0]);
++   }
++   return 0;
++}
++
+ #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI */
 Index: src/stored/bextract.c
 ===================================================================
---- src/stored/bextract.c      (révision 6443)
+--- src/stored/bextract.c      (révision 6467)
 +++ src/stored/bextract.c      (copie de travail)
 @@ -324,6 +324,14 @@
           Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
@@ -636,9 +816,9 @@ Index: src/stored/bextract.c
 +      /* handle deleted file 
 +       */
 +      if (rec->FileIndex == 0) {
-+       /* if file is included, remove it ? */
-+       Jmsg(jcr, M_INFO, 0, _("fname=%s is marked as deleted.\n", attr->fname));
-+       break;
++         /* if file is included, remove it ? */
++         Jmsg(jcr, M_INFO, 0, _("fname=%s is marked as deleted.\n"), attr->fname);
++         break;
 +      }
 +
        if (attr->file_index != rec->FileIndex) {
@@ -646,19 +826,19 @@ Index: src/stored/bextract.c
              rec->FileIndex, attr->file_index);
 Index: src/stored/bscan.c
 ===================================================================
---- src/stored/bscan.c (révision 6443)
+--- src/stored/bscan.c (révision 6467)
 +++ src/stored/bscan.c (copie de travail)
-@@ -648,6 +648,15 @@
+@@ -660,6 +660,15 @@
     case STREAM_UNIX_ATTRIBUTES:
     case STREAM_UNIX_ATTRIBUTES_EX:
  
 +      /* handle deleted file 
 +       */
 +      if (rec->FileIndex == 0) {
-+       create_file_attributes_record(db, mjcr, attr->fname, attr->lname,
-+                                     FT_NOSTAT, "", rec);
-+       free_jcr(mjcr);
-+       break;
++         create_file_attributes_record(db, mjcr, attr->fname, attr->lname,
++                                       FT_NOSTAT, "", rec);
++         free_jcr(mjcr);
++         break;
 +      }
 +
        if (!unpack_attributes_record(bjcr, rec->Stream, rec->data, attr)) {
@@ -666,18 +846,19 @@ Index: src/stored/bscan.c
        }
 Index: src/stored/append.c
 ===================================================================
---- src/stored/append.c        (révision 6443)
+--- src/stored/append.c        (révision 6467)
 +++ src/stored/append.c        (copie de travail)
-@@ -146,7 +146,7 @@
+@@ -146,7 +146,8 @@
  
        /* Read Stream header from the File daemon.
         *  The stream header consists of the following:
 -       *    file_index (sequential Bacula file index, base 1)
-+       *    file_index (sequential Bacula file index, base 1, 0 for deleted files)
++       *    file_index (sequential Bacula file index, base 1, 
++       *                0 for deleted files)
         *    stream     (Bacula number to distinguish parts of data)
         *    info       (Info for Storage daemon -- compressed, encryped, ...)
         *       info is not currently used, so is read, but ignored!
-@@ -185,16 +185,18 @@
+@@ -185,16 +186,21 @@
  
        Dmsg2(890, "<filed: Header FilInx=%d stream=%d\n", file_index, stream);
  
@@ -686,17 +867,20 @@ Index: src/stored/append.c
 -         Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
 -         ok = false;
 -         break;
-+      if (file_index != 0) {  /* TODO: handle file_index == 0 */
-+       if (!(file_index > 0 && (file_index == last_file_index ||
-+                                file_index == last_file_index + 1))) {
-+          Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
-+          ok = false;
-+          break;
-+       }
-+       if (file_index != last_file_index) {
-+          jcr->JobFiles = file_index;
-+          last_file_index = file_index;
-+       }
++      /*
++       * In accurate mode, files with file_index == 0 are marked as deleted
++       */
++      if (!file_index) {
++         if (!(file_index > 0 && (file_index == last_file_index ||
++                                  file_index == last_file_index + 1))) {
++            Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
++            ok = false;
++            break;
++         }
++         if (file_index != last_file_index) {
++            jcr->JobFiles = file_index;
++            last_file_index = file_index;
++         }
        }
 -      if (file_index != last_file_index) {
 -         jcr->JobFiles = file_index;
@@ -705,20 +889,29 @@ Index: src/stored/append.c
  
        /* Read data stream from the File daemon.
         *  The data stream is just raw bytes
-@@ -212,25 +214,26 @@
-             stream_to_ascii(buf1, rec.Stream,rec.FileIndex),
-             rec.data_len);
+@@ -214,22 +220,23 @@
  
--         while (!write_record_to_block(dcr->block, &rec)) {
--            Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
+          while (!write_record_to_block(dcr->block, &rec)) {
+             Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
 -                       rec.remainder);
--            if (!write_block_to_device(dcr)) {
--               Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
++                  rec.remainder);
+             if (!write_block_to_device(dcr)) {
+                Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
 -                  dev->print_name(), dev->bstrerror());
--               ok = false;
--               break;
--            }
--         }
++                     dev->print_name(), dev->bstrerror());
+                ok = false;
+                break;
+             }
++
++            if (!ok) {
++               Dmsg0(400, "Not OK\n");
++               break;
++            }
++            jcr->JobBytes += rec.data_len;   /* increment bytes this job */
++            Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
++                  FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
++                  stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
+          }
 -         if (!ok) {
 -            Dmsg0(400, "Not OK\n");
 -            break;
@@ -727,32 +920,12 @@ Index: src/stored/append.c
 -         Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
 -            FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
 -            stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
-+       while (!write_record_to_block(dcr->block, &rec)) {
-+          Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
-+                rec.remainder);
-+          if (!write_block_to_device(dcr)) {
-+             Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
-+                   dev->print_name(), dev->bstrerror());
-+             ok = false;
-+             break;
-+          }
-+          if (!ok) {
-+             Dmsg0(400, "Not OK\n");
-+             break;
-+          }
-+          jcr->JobBytes += rec.data_len;   /* increment bytes this job */
-+          Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
-+                FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
-+                stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
-+       }
-+
           /* Send attributes and digest to Director for Catalog */
           if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX ||
-              crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
 Index: src/jcr.h
 ===================================================================
---- src/jcr.h  (révision 6443)
+--- src/jcr.h  (révision 6467)
 +++ src/jcr.h  (copie de travail)
 @@ -119,6 +119,7 @@
  
@@ -770,15 +943,146 @@ Index: src/jcr.h
  #endif /* FILE_DAEMON */
  
  
+Index: src/lib/Makefile.in
+===================================================================
+--- src/lib/Makefile.in        (révision 6467)
++++ src/lib/Makefile.in        (copie de travail)
+@@ -29,7 +29,7 @@
+         res.c rwlock.c scan.c serial.c sha1.c \
+         signal.c smartall.c rblist.c tls.c tree.c \
+         util.c var.c watchdog.c workq.c btimers.c \
+-        address_conf.c pythonlib.c breg.c
++        address_conf.c pythonlib.c breg.c htable.c
+ LIBOBJS = attr.o base64.o berrno.o bsys.o bget_msg.o \
+@@ -42,7 +42,7 @@
+         res.o rwlock.o scan.o serial.o sha1.o \
+         signal.o smartall.o rblist.o tls.o tree.o \
+         util.o var.o watchdog.o workq.o btimers.o \
+-        address_conf.o pythonlib.o breg.o
++        address_conf.o pythonlib.o breg.o htable.o
+ EXTRAOBJS = @OBJLIST@
+Index: src/findlib/find.c
+===================================================================
+--- src/findlib/find.c (révision 6467)
++++ src/findlib/find.c (copie de travail)
+@@ -96,6 +96,13 @@
+   Dmsg0(100, "Leave set_find_options()\n");
+ }
++void
++set_find_changed_function(FF_PKT *ff, bool check_fct(JCR *jcr, FF_PKT *ff))
++{
++   Dmsg0(100, "Enter set_find_changed_function()\n");
++   ff->check_fct = check_fct;
++}
++
+ /*
+  * For VSS we need to know which windows drives
+  * are used, because we create a snapshot of all used
+Index: src/findlib/find_one.c
+===================================================================
+--- src/findlib/find_one.c     (révision 6467)
++++ src/findlib/find_one.c     (copie de travail)
+@@ -258,6 +258,33 @@
+ }
+ /*
++ * In incremental/diffential or accurate backup, we
++ * say if the current file has changed.
++ */
++static bool check_changes(JCR *jcr, FF_PKT *ff_pkt)
++{
++   /* in special mode (like accurate backup), user can 
++    * choose his comparison function.
++    */
++   if (ff_pkt->check_fct) {
++      return ff_pkt->check_fct(jcr, ff_pkt);
++   }
++
++   /* in normal modes (incr/diff), we use this default
++    * behaviour
++    */
++   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))) 
++   {
++      return true;
++   } else {
++      return false;
++   }
++}
++
++/*
+  * Find a single file.
+  * handle_file is the callback for handling the file.
+  * p is the filename
+@@ -333,16 +360,13 @@
+     * since our last "save_time", presumably the last Full save
+     * or Incremental.
+     */
+-   if (ff_pkt->incremental && !S_ISDIR(ff_pkt->statp.st_mode)) {
++   if (   ff_pkt->incremental 
++       && !S_ISDIR(ff_pkt->statp.st_mode) 
++       && !check_changes(jcr, ff_pkt)) 
++   {
+       Dmsg1(300, "Non-directory incremental: %s\n", ff_pkt->fname);
+-      /* Not a directory */
+-      if (ff_pkt->statp.st_mtime < ff_pkt->save_time
+-          && ((ff_pkt->flags & FO_MTIMEONLY) ||
+-              ff_pkt->statp.st_ctime < ff_pkt->save_time)) {
+-         /* Incremental option, file not changed */
+-         ff_pkt->type = FT_NOCHG;
+-         return handle_file(jcr, ff_pkt, top_level);
+-      }
++      ff_pkt->type = FT_NOCHG;
++      return handle_file(jcr, ff_pkt, top_level);
+    }
+ #ifdef HAVE_DARWIN_OS
+@@ -502,15 +526,13 @@
+       link[len] = 0;
+       ff_pkt->link = link;
+-      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))) {
++      if (ff_pkt->incremental && !check_changes(jcr, ff_pkt)) {
+          /* Incremental option, directory entry not changed */
+          ff_pkt->type = FT_DIRNOCHG;
+       } else {
+          ff_pkt->type = FT_DIRBEGIN;
+       }
++
+       /* We have set st_rdev to 1 if it is a reparse point, otherwise 0 */
+       if (have_win32_api() && ff_pkt->statp.st_rdev) {
+          ff_pkt->type = FT_REPARSE;
 Index: src/findlib/find.h
 ===================================================================
---- src/findlib/find.h (révision 6443)
+--- src/findlib/find.h (révision 6467)
 +++ src/findlib/find.h (copie de travail)
-@@ -108,6 +108,7 @@
- #define FO_ENHANCEDWILD (1<<23)       /* Enhanced wild card processing */
- #define FO_CHKCHANGES   (1<<24)       /* Check if file have been modified during backup */
- #define FO_STRIPPATH    (1<<25)       /* Check for stripping path */
-+#define FO_ACCURATE     (1<<26)       /* Accurate mode */
- struct s_included_file {
-    struct s_included_file *next;
+@@ -215,6 +215,7 @@
+    findFILESET *fileset;
+    int (*file_save)(JCR *, FF_PKT *, bool); /* User's callback */
+    int (*plugin_save)(JCR *, FF_PKT *, bool); /* User's callback */
++   bool (*check_fct)(JCR *, FF_PKT *); /* optionnal user fct to check file changes */
+    /* Values set by accept_file while processing Options */
+    uint32_t flags;                    /* backup options */
+Index: src/findlib/protos.h
+===================================================================
+--- src/findlib/protos.h       (révision 6467)
++++ src/findlib/protos.h       (copie de travail)
+@@ -45,6 +45,7 @@
+ /* From find.c */
+ FF_PKT *init_find_files();
+ void  set_find_options(FF_PKT *ff, int incremental, time_t mtime);
++void set_find_changed_function(FF_PKT *ff, bool check_fct(JCR *jcr, FF_PKT *ff));
+ int   find_files(JCR *jcr, FF_PKT *ff, int file_sub(JCR *, FF_PKT *ff_pkt, bool),
+                  int plugin_sub(JCR *, FF_PKT *ff_pkt, bool));
+ int   match_files(JCR *jcr, FF_PKT *ff, int sub(JCR *, FF_PKT *ff_pkt, bool));