]> git.sur5r.net Git - bacula/bacula/blob - bacula/patches/testing/project-accurate-backup.patch
ebl Update accurate backup project
[bacula/bacula] / bacula / patches / testing / project-accurate-backup.patch
1 Index: src/dird/backup.c
2 ===================================================================
3 --- src/dird/backup.c   (révision 6374)
4 +++ src/dird/backup.c   (copie de travail)
5 @@ -44,6 +44,7 @@
6  #include "bacula.h"
7  #include "dird.h"
8  #include "ua.h"
9 +#include "findlib/find.h"
10  
11  /* Commands sent to File daemon */
12  static char backupcmd[] = "backup\n";
13 @@ -97,6 +98,338 @@
14  }
15  
16  /*
17 + * We are called here for each record that matches the above
18 + *  SQL query -- that is for each file contained in the Catalog
19 + *  that was not marked earlier. This means that the file in
20 + *  question is a missing file (in the Catalog but not on Disk).
21 + */
22 +static int missing_handler(void *ctx, int num_fields, char **row)
23 +{
24 +   JCR *jcr = (JCR *)ctx;
25 +
26 +   if (job_canceled(jcr)) {
27 +      return 1;
28 +   }
29 +
30 +   /* TODO: return the list to the FD */
31 +   if (num_fields == 2) 
32 +      Qmsg(jcr, M_INFO, 0, "      %s%s\n", row[0]?row[0]:"", row[1]?row[1]:"");
33 +   else
34 +      Qmsg(jcr, M_INFO, 0, "      %s\n", row[0]?row[0]:"");
35
36 +   return 0;
37 +}
38 +
39 +bool accurate_check_file(JCR *jcr, FILE_DBR *fdbr, char *attr, char *Opts_Digest, int *do_Digest)
40 +{
41 +   char *p;
42 +   int stat=false;
43 +   struct stat statf;                 /* file stat */
44 +   struct stat statc;                 /* catalog stat */
45 +
46 +   int32_t LinkFIf, LinkFIc;
47 +
48 +   decode_stat(attr, &statf, &LinkFIf);  /* decode file stat packet */
49 +   decode_stat(fdbr->LStat, &statc, &LinkFIc); /* decode catalog stat */
50 +   *do_Digest = CRYPTO_DIGEST_NONE;
51 +
52 +   for (p=Opts_Digest; *p; p++) {
53 +      char ed1[30], ed2[30];
54 +      switch (*p) {
55 +      case 'i':                /* compare INODEs */
56 +        if (statc.st_ino != statf.st_ino) {
57 +           Jmsg(jcr, M_INFO, 0, _("      st_ino   differ. Cat: %s File: %s\n"),
58 +                edit_uint64((uint64_t)statc.st_ino, ed1),
59 +                edit_uint64((uint64_t)statf.st_ino, ed2));
60 +           stat = true;
61 +        }
62 +        break;
63 +      case 'p':                /* permissions bits */
64 +        if (statc.st_mode != statf.st_mode) {
65 +           Jmsg(jcr, M_INFO, 0, _("      st_mode  differ. Cat: %x File: %x\n"),
66 +                (uint32_t)statc.st_mode, (uint32_t)statf.st_mode);
67 +           stat = true;
68 +        }
69 +        break;
70 +      case 'n':                /* number of links */
71 +        if (statc.st_nlink != statf.st_nlink) {
72 +           Jmsg(jcr, M_INFO, 0, _("      st_nlink differ. Cat: %d File: %d\n"),
73 +                (uint32_t)statc.st_nlink, (uint32_t)statf.st_nlink);
74 +           stat = true;
75 +        }
76 +        break;
77 +      case 'u':                /* user id */
78 +        if (statc.st_uid != statf.st_uid) {
79 +           Jmsg(jcr, M_INFO, 0, _("      st_uid   differ. Cat: %u File: %u\n"),
80 +                (uint32_t)statc.st_uid, (uint32_t)statf.st_uid);
81 +           stat = true;
82 +        }
83 +        break;
84 +      case 'g':                /* group id */
85 +        if (statc.st_gid != statf.st_gid) {
86 +           Jmsg(jcr, M_INFO, 0, _("      st_gid   differ. Cat: %u File: %u\n"),
87 +                (uint32_t)statc.st_gid, (uint32_t)statf.st_gid);
88 +           stat = true;
89 +        }
90 +        break;
91 +      case 's':                /* size */
92 +        if (statc.st_size != statf.st_size) {
93 +           Jmsg(jcr, M_INFO, 0, _("      st_size  differ. Cat: %s File: %s\n"),
94 +                edit_uint64((uint64_t)statc.st_size, ed1),
95 +                edit_uint64((uint64_t)statf.st_size, ed2));
96 +           stat = true;
97 +        }
98 +        break;
99 +      case 'a':                /* access time */
100 +        if (statc.st_atime != statf.st_atime) {
101 +           Jmsg(jcr, M_INFO, 0, _("      st_atime differs\n"));
102 +           stat = true;
103 +        }
104 +        break;
105 +      case 'm':
106 +        if (statc.st_mtime != statf.st_mtime) {
107 +           Jmsg(jcr, M_INFO, 0, _("      st_mtime differs\n"));
108 +           stat = true;
109 +        }
110 +        break;
111 +      case 'c':                /* ctime */
112 +        if (statc.st_ctime != statf.st_ctime) {
113 +           Jmsg(jcr, M_INFO, 0, _("      st_ctime differs\n"));
114 +           stat = true;
115 +        }
116 +        break;
117 +      case 'd':                /* file size decrease */
118 +        if (statc.st_size > statf.st_size) {
119 +           Jmsg(jcr, M_INFO, 0, _("      st_size  decrease. Cat: %s File: %s\n"),
120 +                edit_uint64((uint64_t)statc.st_size, ed1),
121 +                edit_uint64((uint64_t)statf.st_size, ed2));
122 +           stat = true;
123 +        }
124 +        break;
125 +      case '5':                /* compare MD5 */
126 +        Dmsg1(500, "set Do_MD5 for %s\n", jcr->fname);
127 +        *do_Digest = CRYPTO_DIGEST_MD5;
128 +        break;
129 +      case '1':                 /* compare SHA1 */
130 +        *do_Digest = CRYPTO_DIGEST_SHA1;
131 +        break;
132 +      case ':':
133 +      case 'V':
134 +      default:
135 +        break;
136 +      }
137 +   }
138 +   return stat;
139 +}
140 +
141 +/*
142 + * Accurate backup mode
143 + * 1. Receive the list of all files including those backed up to the Dir
144 + * 2. Dir computes files and deleted files.
145 + * 3. Dir sends list of additional files (new files) to backup, and list of files
146 + *    deleted.
147 + *
148 + * Cleanup attributes (don't use atime, inode etc..)
149 + * Need to insert file and attributes to temp table ?
150 + * Batch compare files and attributes ?
151 + *
152 + * If file have file_index=0, they are discarded by FD
153 + *
154 + * TODO: send deleted list and new list to client
155 + *       initialize currentlist and currentbackupid tables
156 + *       tweak SD with file_index=-1
157 + */
158 +bool accurate_compute_files(JCR *jcr)
159 +{
160 +   BSOCK   *fd;
161 +   int n, len;
162 +   FILE_DBR fdbr;
163 +   char buf[MAXSTRING];
164 +   char ed1[50], ed2[50];
165 +   POOLMEM *fname = get_pool_memory(PM_MESSAGE);
166 +   int do_Digest = CRYPTO_DIGEST_NONE;
167 +   int32_t file_index = 0;
168 +   JobId_t JobId=0;            /* TODO: compute the job key in new table */
169 +   JobId_t backupid=0;
170 +
171 +   memset(&fdbr, 0, sizeof(FILE_DBR));
172 +   fd = jcr->file_bsock;
173 +   fdbr.JobId = JobId;         
174 +   jcr->FileIndex = 0;
175 +
176 +   if (/*jcr->accurate == false ||*/ jcr->JobLevel == L_FULL) {
177 +      return true;
178 +   }
179 +
180 +   backupid = db_find_backupid(jcr, jcr->db, &jcr->jr);
181 +   if (!backupid) {
182 +      Jmsg(jcr, M_ERROR, 0, _("Can't use Accurate mode ERR=Can't find BackupId\n"));
183 +      return false;
184 +   }
185 +   db_accurate_create_backup_table(jcr, jcr->db, jcr->JobId);
186 +   Dmsg0(1, "bdird: waiting to receive file attributes\n");
187 +   /*
188 +    * Get Attributes and Signature from File daemon
189 +    * We expect:
190 +    *   FileIndex
191 +    *   Stream
192 +    *   Options or Digest (MD5/SHA1)
193 +    *   Filename
194 +    *   Attributes
195 +    *   Link name  ???
196 +    */
197 +   while ((n=bget_dirmsg(fd)) >= 0 && !job_canceled(jcr)) {
198 +      int stream;
199 +      char *attr, *p, *fn;
200 +      char Opts_Digest[MAXSTRING];        /* Verify Opts or MD5/SHA1 digest */
201 +
202 +      if (job_canceled(jcr)) {
203 +         goto bail_out2;
204 +      }
205 +      fname = check_pool_memory_size(fname, fd->msglen);
206 +      jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
207 +      Dmsg1(1, "Atts+Digest=%s\n", fd->msg);
208 +      if ((len = sscanf(fd->msg, "%ld %d %100s", &file_index, &stream,
209 +            fname)) != 3) {
210 +         Jmsg3(jcr, M_FATAL, 0, _("bird<filed: bad attributes, expected 3 fields got %d\n"
211 +" mslen=%d msg=%s\n"), len, fd->msglen, fd->msg);
212 +         goto bail_out2;
213 +      }
214 +      /*
215 +       * We read the Options or Signature into fname
216 +       *  to prevent overrun, now copy it to proper location.
217 +       */
218 +      bstrncpy(Opts_Digest, fname, sizeof(Opts_Digest));
219 +      p = fd->msg;
220 +      skip_nonspaces(&p);             /* skip FileIndex */
221 +      skip_spaces(&p);
222 +      skip_nonspaces(&p);             /* skip Stream */
223 +      skip_spaces(&p);
224 +      skip_nonspaces(&p);             /* skip Opts_Digest */
225 +      p++;                            /* skip space */
226 +      fn = fname;
227 +      while (*p != 0) {
228 +         *fn++ = *p++;                /* copy filename */
229 +      }
230 +      *fn = *p++;                     /* term filename and point to attribs */
231 +      attr = p;
232 +      /*
233 +       * Got attributes stream, decode it
234 +       */
235 +      if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
236 +        int changed=true;
237 +         Dmsg2(400, "file_index=%d attr=%s\n", file_index, attr);
238 +         jcr->JobFiles++;
239 +         jcr->FileIndex = file_index;    /* remember attribute file_index */
240 +         do_Digest = CRYPTO_DIGEST_NONE;
241 +         pm_strcpy(jcr->fname, fname);  /* move filename into JCR */
242 +        fdbr.FileId = 0;
243 +
244 +         /*
245 +          * Find equivalent record in the database
246 +          */
247 +        
248 +        if (db_accurate_get_file_attributes_record(jcr, jcr->db, jcr->fname,
249 +                                                   backupid, &fdbr))
250 +        {
251 +           Dmsg2(1, "get_file ok fname=%s fileid=%i\n", jcr->fname, fdbr.FileId);
252 +
253 +           if (file_index != 0) {
254 +              changed = accurate_check_file(jcr, &fdbr, attr, Opts_Digest, &do_Digest);
255 +              Dmsg1(1, "check_file changed=%i\n", changed);
256 +
257 +              if (changed == true) {
258 +                 db_accurate_mark_file_for_backup(jcr, jcr->db, jcr->fname, jcr->JobId);
259 +                 db_accurate_delete_file_record(jcr, jcr->db, fdbr.FileId,
260 +                                                backupid);
261 +              }
262 +           }
263 +
264 +           if (file_index == 0 || changed == false) {
265 +              Dmsg1(1, "mark_file fileid=%i\n", fdbr.FileId);
266 +              db_accurate_mark_file_record(jcr, jcr->db, backupid,
267 +                                           fdbr.FileId, jcr->JobId);
268 +           }
269 +
270 +        } else if (file_index == 0) {
271 +           Dmsg1(1, "mark_for_backup fname=%s\n", jcr->fname);
272 +           db_accurate_mark_file_for_backup(jcr, jcr->db, jcr->fname, jcr->JobId);
273 +        }
274 +        
275 +
276 +      /*
277 +       * Got Digest Signature from Storage daemon
278 +       *  It came across in the Opts_Digest field.
279 +       */
280 +      } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
281 +         Dmsg2(400, "stream=Digest inx=%d Digest=%s\n", file_index, Opts_Digest);
282 +         /*
283 +          * When ever we get a digest it MUST have been
284 +          * preceded by an attributes record, which sets attr_file_index
285 +          */
286 +         if (jcr->FileIndex != (uint32_t)file_index) {
287 +            Jmsg2(jcr, M_FATAL, 0, _("MD5/SHA1 index %d not same as attributes %d\n"),
288 +               file_index, jcr->FileIndex);
289 +            goto bail_out2;
290 +         }
291 +         if (do_Digest != CRYPTO_DIGEST_NONE) {
292 +            db_escape_string(jcr, jcr->db, buf, Opts_Digest, strlen(Opts_Digest));
293 +            if (strcmp(buf, fdbr.Digest) != 0) {
294 +               if (debug_level >= 10) {
295 +                  Jmsg(jcr, M_INFO, 0, _("      %d not same. File=%s Cat=%s\n"),
296 +                       stream, buf, fdbr.Digest);
297 +               } else {
298 +                  Jmsg(jcr, M_INFO, 0, _("      %d differs.\n"),
299 +                       stream);
300 +               }
301 +               //stat = JS_Differences;
302 +            }
303 +            do_Digest = CRYPTO_DIGEST_NONE;
304 +         }
305 +      }
306 +//      jcr->JobFiles = file_index;
307 +   }
308 +   if (is_bnet_error(fd)) {
309 +      berrno be;
310 +      Jmsg2(jcr, M_FATAL, 0, _("bdird<filed: bad attributes from filed n=%d : %s\n"),
311 +                        n, be.bstrerror());
312 +      goto bail_out2;
313 +   }
314 +
315 +   /* Now find all the files that are missing -- i.e. all files in
316 +    *  the database where the MarkId != current JobId
317 +    */
318 +
319 +   bsnprintf(buf, sizeof(buf),
320 +      "SELECT Path.Path,Filename.Name "
321 +        "FROM CurrentFile "
322 +             "JOIN File USING (FileId) "
323 +             "JOIN Path USING (PathId) "
324 +             "JOIN Filename USING (FilenameId) "
325 +      "WHERE CurrentFile.BackupId=%s "
326 +        "AND CurrentFile.MarkId!=%s ",
327 +            edit_uint64(backupid, ed1), edit_uint64(jcr->JobId, ed2));
328 +   /* missing_handler is called for each file found */
329 +   Dmsg1(1, "display deleted files cmd=%s\n", buf);
330 +   db_sql_query(jcr->db, buf, missing_handler, (void *)jcr);
331 +
332 +   bsnprintf(buf, sizeof(buf),
333 +            "SELECT Name FROM ToBackup%s",
334 +            edit_uint64(jcr->JobId, ed2));
335 +   /* missing_handler is called for each file found */
336 +   Dmsg1(1, "display files to backup cmd=%s\n", buf);
337 +   db_sql_query(jcr->db, buf, missing_handler, (void *)jcr);
338 +
339 +   free_pool_memory(fname);
340 +
341 +   return true;
342 +
343 +bail_out2:
344 +   db_accurate_drop_backup_table(jcr, jcr->db, jcr->JobId);
345 +   return false;
346 +}
347 +
348 +/*
349   * Do a backup of the specified FileSet
350   *
351   *  Returns:  false on failure
352 @@ -231,6 +564,13 @@
353        goto bail_out;
354     }
355  
356 +   /*
357 +    * If backup is in accurate mode, FD will send the list of
358 +    * all files. We have to store it, and compute witch files
359 +    * have been deleted and witch files have to be backuped.
360 +    */
361 +   accurate_compute_files(jcr);
362 +
363     /* Pickup Job termination data */
364     stat = wait_for_job_termination(jcr);
365     db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
366 Index: src/dird/inc_conf.c
367 ===================================================================
368 --- src/dird/inc_conf.c (révision 6374)
369 +++ src/dird/inc_conf.c (copie de travail)
370 @@ -94,6 +94,7 @@
371   * Items that are valid in an Options resource
372   */
373  static RES_ITEM options_items[] = {
374 +   {"accurate",        store_opts,    {0},     0, 0, 0},
375     {"compression",     store_opts,    {0},     0, 0, 0},
376     {"signature",       store_opts,    {0},     0, 0, 0},
377     {"verify",          store_opts,    {0},     0, 0, 0},
378 @@ -153,7 +154,8 @@
379     INC_KW_NOATIME,
380     INC_KW_ENHANCEDWILD,
381     INC_KW_CHKCHANGES,
382 -   INC_KW_STRIPPATH
383 +   INC_KW_STRIPPATH,
384 +   INC_KW_ACCURATE
385  };
386  
387  /*
388 @@ -163,6 +165,7 @@
389   *   options given above.
390   */
391  static struct s_kw FS_option_kw[] = {
392 +   {"accurate",    INC_KW_ACCURATE},
393     {"compression", INC_KW_COMPRESSION},
394     {"signature",   INC_KW_DIGEST},
395     {"encryption",  INC_KW_ENCRYPTION},
396 @@ -251,6 +254,8 @@
397     {"no",       INC_KW_ENHANCEDWILD,  "0"},
398     {"yes",      INC_KW_CHKCHANGES,    "c"},
399     {"no",       INC_KW_CHKCHANGES,    "0"},
400 +   {"yes",      INC_KW_ACCURATE,      "C"},
401 +   {"no",       INC_KW_ACCURATE,      "0"},
402     {NULL,       0,                      0}
403  };
404  
405 Index: src/dird/dird_conf.c
406 ===================================================================
407 --- src/dird/dird_conf.c        (révision 6374)
408 +++ src/dird/dird_conf.c        (copie de travail)
409 @@ -319,6 +319,7 @@
410     {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
411     {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
412     {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
413 +   {"accuratebackup", store_bool, ITEM(res_job.accurate), 0,0,0},
414     {NULL, NULL, {0}, 0, 0, 0}
415  };
416  
417 @@ -618,6 +619,9 @@
418        if (res->res_job.spool_size) {
419           sendit(sock, _("     SpoolSize=%s\n"),        edit_uint64(res->res_job.spool_size, ed1));
420        }
421 +      if (res->res_job.JobType == JT_BACKUP) {
422 +        sendit(sock, _("     Accurate=%d\n"), res->res_job.accurate);
423 +      }
424        if (res->res_job.JobType == JT_MIGRATE) {
425           sendit(sock, _("     SelectionType=%d\n"), res->res_job.selection_type);
426        }
427 Index: src/dird/dird_conf.h
428 ===================================================================
429 --- src/dird/dird_conf.h        (révision 6374)
430 +++ src/dird/dird_conf.h        (copie de travail)
431 @@ -400,6 +400,7 @@
432     bool write_part_after_job;         /* Set to write part after job in SD */
433     bool enabled;                      /* Set if job enabled */
434     bool OptimizeJobScheduling;        /* Set if we should optimize Job scheduling */
435 +   bool accurate;                     /* Set if it is an accurate backup job */
436     
437     MSGS      *messages;               /* How and where to send messages */
438     SCHED     *schedule;               /* When -- Automatic schedule */
439 Index: src/filed/backup.c
440 ===================================================================
441 --- src/filed/backup.c  (révision 6374)
442 +++ src/filed/backup.c  (copie de travail)
443 @@ -50,6 +50,98 @@
444  static bool crypto_session_send(JCR *jcr, BSOCK *sd);
445  
446  /*
447 + * Called by save_file when accept/discard file for backup
448 + *
449 + * TODO: we could add MD5/SHAX digest, but we have to compute it
450 + * for all files.
451 + */
452 +static bool accurate_add_file(JCR *jcr, FF_PKT *ff_pkt, char *stats)
453 +{
454 +   char *a=stats;
455 +   char attribs[MAXSTRING];
456 +   uint32_t file_index=jcr->JobFiles;
457 +   BSOCK *dir = jcr->dir_bsock;
458 +   int stat;
459 +
460 +   if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
461 +      return true;
462 +   }
463 +
464 +   if (!stats) {               /* TODO: don't always compute attribute  */
465 +      file_index=0;
466 +      encode_stat(attribs, ff_pkt, 0);
467 +      a = attribs;
468 +   }
469 +
470 +   switch (ff_pkt->type) {
471 +   case FT_LNKSAVED:                  /* Hard linked, file already saved */
472 +   case FT_LNK:
473 +      stat = dir->fsend("%d %d %s %s%c%s%c%s%c", file_index,
474 +            STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname,
475 +            0, a, 0, ff_pkt->link, 0);
476 +      break;
477 +   case FT_REGE:
478 +   case FT_REG:
479 +   case FT_SPEC:
480 +   case FT_RAW:
481 +   case FT_FIFO:
482 +   case FT_NOCHG:
483 +      stat = dir->fsend("%d %d %s %s%c%s%c%c", file_index,
484 +            STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname,
485 +            0, a, 0, 0);
486 +
487 +      break;
488 +   case FT_REPARSE: 
489 +   case FT_DIREND:
490 +   case FT_NORECURSE:
491 +   case FT_DIRNOCHG:
492 +      stat = dir->fsend("%d %d %s %s%c%s%c%c", file_index,
493 +               STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->link,
494 +               0, a, 0, 0);
495 +      break;
496 +   default:
497 +      Dmsg2(1, _("Fname=%s Type=%i\n"), ff_pkt->fname, ff_pkt->type);
498 +      return true;
499 +   }
500 +
501 +   if (!stat) {
502 +      Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), bnet_strerror(dir));
503 +      return 0;
504 +   }
505 +
506 +   return true;
507 +}
508 +
509 +/* build a fileset with new files from director */
510 +static bool accurate_get_new_and_deleted_file_list(JCR *jcr)
511 +{   
512 +   if (jcr->accurate == false || job_canceled(jcr)) {
513 +      return true;
514 +   }
515 +   return true;
516 +}
517 +
518 +/* send deleted file list to stored */
519 +static bool accurate_send_deleted_list(JCR *jcr)
520 +{
521 +   if (jcr->accurate == false || job_canceled(jcr)) {
522 +      return true;
523 +   }
524 +   return true;
525 +}
526 +
527 +static bool accurate_send_file_list(JCR *jcr)
528 +{
529 +   if (jcr->accurate == false || job_canceled(jcr)) {
530 +      return true;
531 +   }
532 +   Dmsg0(1, "Sending BNET_EOD\n");
533 +   jcr->dir_bsock->signal(BNET_EOD);            /* end of sending data */
534 +   return true;
535 +}
536 +
537 +
538 +/*
539   * Find all the requested files and send them
540   * to the Storage daemon.
541   *
542 @@ -66,6 +158,9 @@
543     BSOCK *sd;
544     bool ok = true;
545     // TODO landonf: Allow user to specify encryption algorithm
546 +   if (jcr->JobLevel != L_FULL) {
547 +      jcr->accurate=true;              /* TODO: remove that */
548 +   }
549  
550     sd = jcr->store_bsock;
551  
552 @@ -134,6 +229,20 @@
553        ok = false;                     /* error */
554        set_jcr_job_status(jcr, JS_ErrorTerminated);
555     }
556 +   Dmsg1(1, "jcr->accurate == %i\n", jcr->accurate);
557 +   /* start accurate stuffs */
558 +   if (jcr->accurate) {
559 +      /* TODO: test job_canceled() */
560 +      accurate_send_file_list(jcr);                 /* send all files to DIR */
561 +      accurate_get_new_and_deleted_file_list(jcr);  /* get a new incr fileset from DIR */
562 +//      set_find_options((FF_PKT *)jcr->ff, 0, 0);            /* we backup all that director wants */
563 +//      if (!find_files(jcr, (FF_PKT *)jcr->ff, save_file, (void *)jcr)) {
564 +//      ok = false;                     /* error */
565 +//      set_jcr_job_status(jcr, JS_ErrorTerminated);
566 +//      }
567 +//      accurate_send_file_list(jcr);                 /* send all new files to DIR */
568 +      accurate_send_deleted_list(jcr);              /* send deleted list to SD  */
569 +   }
570  
571     free_pool_memory(jcr->acl_text);
572  
573 @@ -355,9 +464,11 @@
574     case FT_DIRNOCHG:
575     case FT_NOCHG:
576        Jmsg(jcr, M_SKIPPED, 1, _("     Unchanged file skipped: %s\n"), ff_pkt->fname);
577 +      accurate_add_file(jcr, ff_pkt, NULL); /* list skipped files */
578        return 1;
579     case FT_ISARCH:
580        Jmsg(jcr, M_NOTSAVED, 0, _("     Archive file not saved: %s\n"), ff_pkt->fname);
581 +      accurate_add_file(jcr, ff_pkt, NULL); /* list skipped files */
582        return 1;
583     case FT_NOOPEN: {
584        berrno be;
585 @@ -1111,6 +1222,9 @@
586     }
587     unstrip_path(ff_pkt);
588  
589 +   /* list backuped files */
590 +   accurate_add_file(jcr, ff_pkt, attribs);
591 +
592     Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
593     if (!stat) {
594        Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
595 Index: src/filed/job.c
596 ===================================================================
597 --- src/filed/job.c     (révision 6374)
598 +++ src/filed/job.c     (copie de travail)
599 @@ -1087,6 +1087,9 @@
600        case 'c':
601           fo->flags |= FO_CHKCHANGES;
602           break;
603 +      case 'C':
604 +         fo->flags |= FO_ACCURATE;
605 +         break;
606        default:
607           Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
608           break;
609 Index: src/cats/sql_update.c
610 ===================================================================
611 --- src/cats/sql_update.c       (révision 6374)
612 +++ src/cats/sql_update.c       (copie de travail)
613 @@ -88,6 +88,76 @@
614     return stat;
615  }
616  
617 +int db_accurate_delete_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t BackupId)
618 +{
619 +   int stat;
620 +   char ed1[50], ed2[50];
621 +   db_lock(mdb);
622 +   Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE FileId=%s AND BackupId=%s",
623 +       edit_int64(FileId, ed1), edit_int64(BackupId, ed2));
624 +   stat = QUERY_DB(jcr, mdb, mdb->cmd);
625 +   db_unlock(mdb);
626 +   return stat;
627 +}
628 +
629 +int db_accurate_mark_file_for_backup(JCR *jcr, B_DB *mdb, char *fname, JobId_t JobId)
630 +{
631 +   int stat;
632 +   int len=strlen(fname);
633 +   char ed1[50];
634 +   db_lock(mdb);
635 +   /* TODO: mdb->esc_xxx are already ok but it's more smart to recompute it */
636 +//   mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*len+2);
637 +//   mdb->esc_name = db_escape_string(jcr, mdb, mdb->esc_name, fname, len);
638 +   Mmsg(mdb->cmd, "INSERT INTO ToBackup%s (name) VALUES ('%s%s')", edit_int64(JobId, ed1), mdb->esc_path, mdb->esc_name);
639 +   stat = INSERT_DB(jcr, mdb, mdb->cmd);
640 +   db_unlock(mdb);
641 +   return stat;
642 +}
643 +
644 +int db_accurate_create_backup_table(JCR *jcr, B_DB *mdb, JobId_t JobId)
645 +{
646 +   int stat;
647 +   char ed1[50];
648 +   db_lock(mdb);
649 +   Mmsg(mdb->cmd, "CREATE TABLE ToBackup%s (name text)", edit_int64(JobId, ed1));
650 +//   Mmsg(mdb->cmd, "CREATE TEMPORARY TABLE ToBackup%s (name text)", edit_int64(JobId, ed1));
651 +   stat = QUERY_DB(jcr, mdb, mdb->cmd);
652 +   db_unlock(mdb);
653 +   return stat;
654 +}
655 +
656 +int db_accurate_drop_backup_table(JCR *jcr, B_DB *mdb, JobId_t JobId)
657 +{
658 +   int stat;
659 +   char ed1[50];
660 +   db_lock(mdb);
661 +//   Mmsg(mdb->cmd, "DROP TABLE ToBackup%s", edit_int64(JobId, ed1));
662 +//   stat = QUERY_DB(jcr, mdb, mdb->cmd);
663 +   db_unlock(mdb);
664 +   return stat;
665 +}
666 +
667 +
668 +/* Mark the file record as being visited during database
669 + * accurate compare. Stuff JobId into the MarkId field
670 + */
671 +int db_accurate_mark_file_record(JCR *jcr, B_DB *mdb, JobId_t BackupId, FileId_t FileId, JobId_t JobId)
672 +{
673 +   int stat;
674 +   char ed1[50], ed2[50], ed3[50];
675 +
676 +   db_lock(mdb);
677 +   Mmsg(mdb->cmd, "UPDATE CurrentFile SET MarkId=%s WHERE FileId=%s AND BackupId=%s", 
678 +       edit_int64(JobId, ed1), edit_int64(FileId, ed2), edit_int64(BackupId, ed3));
679 +   stat = QUERY_DB(jcr, mdb, mdb->cmd);
680 +   if (!stat || sql_affected_rows(mdb) != 1) {
681 +      stat = 0;
682 +   }
683 +   db_unlock(mdb);
684 +   return stat;
685 +}
686 +
687  /*
688   * Update the Job record at start of Job
689   *
690 Index: src/cats/make_postgresql_tables.in
691 ===================================================================
692 --- src/cats/make_postgresql_tables.in  (révision 6374)
693 +++ src/cats/make_postgresql_tables.in  (copie de travail)
694 @@ -43,6 +43,59 @@
695  CREATE INDEX file_jobid_idx on file (jobid);
696  CREATE INDEX file_fp_idx on file (filenameid, pathid);
697  
698 +CREATE TABLE CurrentBackupId
699 +(
700 +     BackupId          serial     not null,
701 +     ClientId          integer    not null,
702 +     JobName           text       not null,
703 +     FileSetId         integer    not null,
704 +     primary key (BackupId)
705 +);
706 +
707 +-- Serait bien de prendre la meme table pour
708 +-- les File et le CurrentBackup...
709 +-- Mais y'a des problemes pour les prunes
710 +
711 +CREATE TABLE CurrentFile
712 +(
713 +     FileId           integer    not null,
714 +     BackupId         integer    not null,
715 +     FullMark         char(1)    default 0,
716 +     MarkId           integer    default 0,
717 +     primary key (FileId)
718 +);
719 +
720 +CREATE INDEX currentfile_fileid on CurrentFile (BackupId);
721 +
722 +-- CREATE TEMPORARY TABLE batch (fileindex int,
723 +--                               jobid int,
724 +--                               path varchar,
725 +--                               name varchar,
726 +--                               lstat varchar,
727 +--                               md5 varchar);
728 +-- 
729 +-- -- On batch insert dans la table temporaire
730 +
731 +-- il faut trouver les fichiers manquant
732 +-- INSERT des nouveaux, UPDATE des anciens, SELECT pour trouver les deletes
733 +
734 +
735 +-- il faut trouver les fichiers modifies
736 +-- Le champs LStat n'est plus le meme
737 +-- SELECT * 
738 +--   FROM CurrentBackup, 
739 +--        batch JOIN Path USING (Path) JOIN Filename USING (Name)
740 +--  WHERE Path.PathId = CurrentBackup.PathId
741 +--    AND Filename.FilenameId = CurrentBackup.FilenameId
742 +--    AND CurrentBackup.LStat != batch.LStat
743 +-- 
744 +-- il faut mettre a jour la liste des fichiers
745 +
746 +
747 +
748 +
749 +
750 +
751  --
752  -- Possibly add one or more of the following indexes
753  --  if your Verifies are too slow.
754 Index: src/cats/protos.h
755 ===================================================================
756 --- src/cats/protos.h   (révision 6374)
757 +++ src/cats/protos.h   (copie de travail)
758 @@ -82,10 +82,12 @@
759  /* sql_find.c */
760  bool db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime);
761  bool db_find_last_jobid(JCR *jcr, B_DB *mdb, const char *Name, JOB_DBR *jr);
762 +JobId_t db_find_backupid(JCR *jcr, B_DB *mdb, JOB_DBR *jr);
763  int db_find_next_volume(JCR *jcr, B_DB *mdb, int index, bool InChanger, MEDIA_DBR *mr);
764  bool db_find_failed_job_since(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM *stime, int &JobLevel);
765  
766  /* sql_get.c */
767 +int db_accurate_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JobId_t backupid, FILE_DBR *fdbr);
768  bool db_get_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pdbr);
769  int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cr);
770  bool db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr);
771 @@ -129,6 +131,11 @@
772  int  db_update_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr);
773  int  db_add_digest_to_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, char *digest, int type);
774  int  db_mark_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t JobId);
775 +int db_accurate_mark_file_for_backup(JCR *jcr, B_DB *mdb, char *fname, FileId_t JobId);
776 +int db_accurate_mark_file_record(JCR *jcr, B_DB *mdb, JobId_t BackupId, FileId_t FileId, JobId_t JobId);
777 +int db_accurate_drop_backup_table(JCR *jcr, B_DB *mdb, JobId_t JobId);
778 +int db_accurate_create_backup_table(JCR *jcr, B_DB *mdb, JobId_t JobId);
779 +int db_accurate_delete_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t BackupId);
780  void db_make_inchanger_unique(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr);
781  
782  #endif /* __SQL_PROTOS_H */
783 Index: src/cats/sql_find.c
784 ===================================================================
785 --- src/cats/sql_find.c (révision 6374)
786 +++ src/cats/sql_find.c (copie de travail)
787 @@ -190,7 +190,55 @@
788     return true;
789  }
790  
791 +/*
792 + * Find BackupId of last job that ran.  E.g. for
793 + *
794 + * Returns: Last backuip
795 + *
796 + */
797 +JobId_t
798 +db_find_backupid(JCR *jcr, B_DB *mdb, JOB_DBR *jr)
799 +{
800 +   SQL_ROW row;
801 +   char ed1[50],ed2[50];
802 +   JobId_t backupid=0;
803  
804 +   /* Find backupid */
805 +   db_lock(mdb);
806 +   Dmsg2(100, "JobLevel=%d JobType=%d\n", jcr->JobLevel, jcr->JobType);
807 +   Mmsg(mdb->cmd,
808 +"SELECT BackupId FROM CurrentBackupId WHERE JobName='%s' AND "
809 +"ClientId=%s AND FileSetId=%s ORDER BY BackupId DESC LIMIT 1",
810 +       jr->Name, 
811 +       edit_int64(jr->ClientId, ed1),
812 +       edit_int64(jr->FileSetId, ed2));
813 +
814 +   Dmsg1(100, "Query: %s\n", mdb->cmd);
815 +   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
816 +      db_unlock(mdb);
817 +      return 0;
818 +   }
819 +   if ((row = sql_fetch_row(mdb)) == NULL) {
820 +      Mmsg1(&mdb->errmsg, _("No Job found for: %s.\n"), mdb->cmd);
821 +      sql_free_result(mdb);
822 +      db_unlock(mdb);
823 +      return 0;
824 +   }
825 +
826 +   backupid = str_to_int64(row[0]);
827 +   sql_free_result(mdb);
828 +
829 +   if (backupid <= 0) {
830 +      Mmsg1(&mdb->errmsg, _("No Job found for: %s\n"), mdb->cmd);
831 +      db_unlock(mdb);
832 +      return 0;
833 +   }
834 +
835 +   db_unlock(mdb);
836 +   return backupid;
837 +}
838 +
839 +
840  /*
841   * Find JobId of last job that ran.  E.g. for
842   *   VERIFY_CATALOG we want the JobId of the last INIT.
843 Index: src/cats/sql_create.c
844 ===================================================================
845 --- src/cats/sql_create.c       (révision 6374)
846 +++ src/cats/sql_create.c       (copie de travail)
847 @@ -829,6 +829,14 @@
848     return true;
849  }
850  
851 +bool db_accurate_insert(JCR *jcr, B_DB *mdb, bool saved, const char *fname, struct stat *stat)
852 +{
853 +   int len;
854 +   split_path_and_file(jcr, mdb, fname);
855 +   /* make like in Verify code */
856 +   return true;
857 +} 
858 +
859  /*
860   * Create File record in B_DB
861   *
862 Index: src/cats/sql_get.c
863 ===================================================================
864 --- src/cats/sql_get.c  (révision 6374)
865 +++ src/cats/sql_get.c  (copie de travail)
866 @@ -66,6 +66,8 @@
867   *
868   *  Returns: 0 on failure
869   *           1 on success with the File record in FILE_DBR
870 + *
871 + * TODO: optimize this with only one query
872   */
873  int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JOB_DBR *jr, FILE_DBR *fdbr)
874  {
875 @@ -86,7 +88,73 @@
876     return stat;
877  }
878  
879 +/*
880 + * Given a full filename (with path), look up the File record
881 + * (with attributes) in the database.
882 + *
883 + *  Returns: 0 on failure
884 + *           1 on success with the File record in FILE_DBR
885 + */
886 +int db_accurate_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JobId_t backupid, FILE_DBR *fdbr)
887 +{
888 +   int stat=0;
889 +   char ed1[50];
890 +   SQL_ROW row;
891  
892 +   db_lock(mdb);
893 +   split_path_and_file(jcr, mdb, fname);
894 +
895 +   mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->fnl+2);
896 +   db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
897 +
898 +   mdb->esc_path = check_pool_memory_size(mdb->esc_path, 2*mdb->pnl+2);
899 +   db_escape_string(jcr, mdb, mdb->esc_path, mdb->path, mdb->pnl);
900 +
901 +   Mmsg(mdb->cmd,
902 +"SELECT FileId, LStat, MD5, FilenameId, PathId, FileIndex, CurrentFile.MarkId, JobId "
903 +  "FROM File JOIN CurrentFile USING (FileId) "
904 +            "JOIN Filename USING (FilenameId) "
905 +            "JOIN Path     USING (PathId) "
906 + "WHERE Path.Path='%s' "
907 +   "AND Filename.Name='%s' "
908 +   "AND BackupId=%s ",
909 +       mdb->esc_path,
910 +       mdb->esc_name,
911 +       edit_int64(backupid, ed1));
912 +   
913 +   Dmsg1(100,"get_file %s\n", mdb->cmd);
914 +
915 +   if (QUERY_DB(jcr, mdb, mdb->cmd)) {
916 +      char ed1[30];
917 +      mdb->num_rows = sql_num_rows(mdb);
918 +      if (mdb->num_rows == 1) {
919 +         if ((row = sql_fetch_row(mdb)) == NULL) {
920 +            Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
921 +         } else {
922 +           fdbr->FileId     = str_to_int64(row[0]);
923 +            bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat));
924 +            bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest));
925 +           fdbr->FilenameId = str_to_int64(row[3]);
926 +           fdbr->PathId     = str_to_int64(row[4]);
927 +           fdbr->FileIndex  = str_to_int64(row[5]);
928 +           fdbr->MarkId     = str_to_int64(row[6]);
929 +           fdbr->JobId      = str_to_int64(row[7]);
930 +           stat=1;
931 +        }
932 +      } else if (mdb->num_rows > 1) {
933 +        Mmsg2(mdb->errmsg, _("Get DB File record %s failed num=%i\n"),fname,mdb->num_rows);
934 +         Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
935 +      }
936 +      sql_free_result(mdb);
937 +   } else {
938 +      Mmsg(mdb->errmsg, _("File record: %s not found in Catalog for BackupId=%s.\n"), fname, ed1);
939 +   }
940 +
941 +   db_unlock(mdb);
942 +
943 +   return stat;
944 +}
945 +
946  /*
947   * Get a File record
948   * Returns: 0 on failure
949 Index: src/jcr.h
950 ===================================================================
951 --- src/jcr.h   (révision 6374)
952 +++ src/jcr.h   (copie de travail)
953 @@ -208,6 +208,7 @@
954     B_DB *db_batch;                    /* database pointer for batch insert */
955     ATTR_DBR *ar;                      /* DB attribute record */
956     guid_list *id_list;                /* User/group id to name list */
957 +   bool accurate;                     /* true if job is accurate */
958  
959     void *plugin_ctx_list;             /* list of contexts for plugins */
960     void *plugin_ctx;                  /* current plugin context */
961 Index: src/findlib/find.h
962 ===================================================================
963 --- src/findlib/find.h  (révision 6374)
964 +++ src/findlib/find.h  (copie de travail)
965 @@ -108,6 +108,7 @@
966  #define FO_ENHANCEDWILD (1<<23)       /* Enhanced wild card processing */
967  #define FO_CHKCHANGES   (1<<24)       /* Check if file have been modified during backup */
968  #define FO_STRIPPATH    (1<<25)       /* Check for stripping path */
969 +#define FO_ACCURATE     (1<<26)       /* Accurate mode */
970  
971  struct s_included_file {
972     struct s_included_file *next;