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