]> git.sur5r.net Git - bacula/bacula/blob - bacula/patches/testing/project-accurate-backup.patch2
ebl new version of accurate project
[bacula/bacula] / bacula / patches / testing / project-accurate-backup.patch2
1 Index: src/dird/fd_cmds.c
2 ===================================================================
3 --- src/dird/fd_cmds.c  (révision 6443)
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 @@ -226,7 +226,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 @@ -240,24 +240,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 6443)
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 @@ -96,7 +97,81 @@
66     return true;
67  }
68  
69 +static int accurate_list_handler(void *ctx, int num_fields, char **row)
70 +{
71 +   JCR *jcr = (JCR *)ctx;
72 +
73 +   if (job_canceled(jcr)) {
74 +      return 1;
75 +   }
76 +   
77 +   if (row[0] > 0) {
78 +      jcr->file_bsock->fsend("%s%s%c%s", row[1], row[2], 0, row[3]); 
79 +   }
80 +   return 0;
81 +}
82 +
83 +bool db_accurate_get_jobids(JCR *jcr, POOLMEM *jobids)
84 +{
85 +   pm_strcpy(jobids, "1");
86 +   return 1;
87 +}
88 +
89 +bool send_accurate_current_files(JCR *jcr)
90 +{
91 +   char buf[MAXSTRING];
92 +   char ed1[50], ed2[50];
93 +
94 +   POOLMEM *jobids = get_pool_memory(PM_FNAME);
95 +   db_accurate_get_jobids(jcr, jobids);
96 +
97 +   bsnprintf(buf, sizeof(buf),
98 +             "CREATE TEMPORARY TABLE btemp2%s AS ( "
99 +             "SELECT max(FileId) as FileId, PathId, FilenameId "
100 +             "FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (%s)) AS F "
101 +             "GROUP BY PathId, FilenameId ) ",
102 +             edit_uint64(jcr->JobId, ed1),
103 +             jobids);
104 +   db_sql_query(jcr->db, buf, NULL, NULL);
105 +
106 +   // TODO: compter le nombre de rows 
107 +   jcr->file_bsock->fsend("accurate files=%s", edit_uint64(5895, ed2)); /* TODO: change protocol to something like nb= */
108 +
109 +   bsnprintf(buf, sizeof(buf),
110 +            "SELECT File.FileIndex, Path.Path, Filename.Name, File.LStat "
111 +              "FROM btemp2%s JOIN Path USING (PathId) JOIN Filename USING (FilenameId) "
112 +              "JOIN File USING (FileId) "
113 +             "WHERE File.FileIndex > 0",
114 +            ed1, jobids);
115 +   db_sql_query(jcr->db, buf, accurate_list_handler, (void *)jcr);
116 +
117 +   bsnprintf(buf, sizeof(buf), "DROP TABLE btemp2%s", ed1);
118 +   free_pool_memory(jobids);
119 +
120  /*
121 + CREATE TEMPORARY TABLE btemp2 AS (
122 +  SELECT max(FileId) as FileId, PathId, FilenameId 
123 +    FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (39867,40341)) AS F
124 +   GROUP BY PathId, FilenameId )
125 +
126 +  SELECT File.FileIndex, Path.Path, Filename.Name, File.LStat
127 +    FROM btemp2 JOIN Path USING (PathId) JOIN Filename USING (FilenameId)
128 +                JOIN File USING (FileId)
129 +   WHERE File.FileIndex > 0
130 +
131 + DROP TABLE btemp2
132 +*/
133 +/*
134 +SELECT DISTINCT ON (PathId, FilenameId) FileIndex, Path, Name, LStat
135 +  FROM File JOIN Filename USING (FilenameId) JOIN Path USING (PathId) WHERE JobId IN (40341)
136 + ORDER BY PathId, FilenameId, JobId DESC
137 +*/
138 +
139 +   jcr->file_bsock->signal(BNET_EOD);
140 +   return true;
141 +}
142 +
143 +/*
144   * Do a backup of the specified FileSet
145   *
146   *  Returns:  false on failure
147 @@ -225,6 +300,14 @@
148        Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
149     }
150  
151 +   /*
152 +    * If backup is in accurate mode, FD will send the list of
153 +    * all files.
154 +    */
155 +   if (!send_accurate_current_files(jcr)) {
156 +      goto bail_out;
157 +   }
158 +
159     /* Send backup command */
160     fd->fsend(backupcmd);
161     if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
162 @@ -234,6 +317,7 @@
163     /* Pickup Job termination data */
164     stat = wait_for_job_termination(jcr);
165     db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
166 +
167     if (stat == JS_Terminated) {
168        backup_cleanup(jcr, stat);
169        return true;
170 Index: src/dird/inc_conf.c
171 ===================================================================
172 --- src/dird/inc_conf.c (révision 6443)
173 +++ src/dird/inc_conf.c (copie de travail)
174 @@ -94,6 +94,7 @@
175   * Items that are valid in an Options resource
176   */
177  static RES_ITEM options_items[] = {
178 +   {"accurate",        store_opts,    {0},     0, 0, 0},
179     {"compression",     store_opts,    {0},     0, 0, 0},
180     {"signature",       store_opts,    {0},     0, 0, 0},
181     {"verify",          store_opts,    {0},     0, 0, 0},
182 @@ -153,7 +154,8 @@
183     INC_KW_NOATIME,
184     INC_KW_ENHANCEDWILD,
185     INC_KW_CHKCHANGES,
186 -   INC_KW_STRIPPATH
187 +   INC_KW_STRIPPATH,
188 +   INC_KW_ACCURATE
189  };
190  
191  /*
192 @@ -163,6 +165,7 @@
193   *   options given above.
194   */
195  static struct s_kw FS_option_kw[] = {
196 +   {"accurate",    INC_KW_ACCURATE},
197     {"compression", INC_KW_COMPRESSION},
198     {"signature",   INC_KW_DIGEST},
199     {"encryption",  INC_KW_ENCRYPTION},
200 @@ -251,6 +254,8 @@
201     {"no",       INC_KW_ENHANCEDWILD,  "0"},
202     {"yes",      INC_KW_CHKCHANGES,    "c"},
203     {"no",       INC_KW_CHKCHANGES,    "0"},
204 +   {"yes",      INC_KW_ACCURATE,      "C"},
205 +   {"no",       INC_KW_ACCURATE,      "0"},
206     {NULL,       0,                      0}
207  };
208  
209 Index: src/filed/backup.c
210 ===================================================================
211 --- src/filed/backup.c  (révision 6443)
212 +++ src/filed/backup.c  (copie de travail)
213 @@ -48,8 +48,175 @@
214  static bool crypto_session_start(JCR *jcr);
215  static void crypto_session_end(JCR *jcr);
216  static bool crypto_session_send(JCR *jcr, BSOCK *sd);
217 +static bool encode_and_send_deleted_file(JCR *jcr, char *fname);
218  
219 +
220  /*
221 + * We are called here for each record that matches the above
222 + *  SQL query -- that is for each file contained in the Catalog
223 + *  that was not marked earlier. This means that the file in
224 + *  question is a missing file (in the Catalog but not on Disk).
225 + */
226 +/* TODO: tweak verify code to use the same function */
227 +bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt, char *attr)
228 +{
229 +   char *p;
230 +   int stat=false;
231 +   struct stat statf;                 /* file stat */
232 +   struct stat statc;                 /* catalog stat */
233 +   char *Opts_Digest = ff_pkt->VerifyOpts;
234 +   char *lstat;
235 +
236 +   int32_t LinkFIf, LinkFIc;
237 +
238 +   if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
239 +      return attr != NULL;     /* if null => don't backup */
240 +   }
241 +   
242 +   return attr != true;
243 +
244 +   decode_stat(attr, &statf, &LinkFIf);  /* decode file stat packet */
245 +   
246 +   // TODO: check for /path/ and /path/file
247 +   lstat = (char *) jcr->file_list->lookup(ff_pkt->fname);
248 +
249 +   if (!lstat) {
250 +      // TODO: we must backup it !
251 +      return true;
252 +   }
253 +
254 +   decode_stat(lstat, &statc, &LinkFIc); /* decode catalog stat */
255 +//   *do_Digest = CRYPTO_DIGEST_NONE;
256 +
257 +   for (p=Opts_Digest; *p; p++) {
258 +      char ed1[30], ed2[30];
259 +      switch (*p) {
260 +      case 'i':                /* compare INODEs */
261 +        if (statc.st_ino != statf.st_ino) {
262 +           Jmsg(jcr, M_INFO, 0, _("      st_ino   differ. Cat: %s File: %s\n"),
263 +                edit_uint64((uint64_t)statc.st_ino, ed1),
264 +                edit_uint64((uint64_t)statf.st_ino, ed2));
265 +           stat = true;
266 +        }
267 +        break;
268 +      case 'p':                /* permissions bits */
269 +        if (statc.st_mode != statf.st_mode) {
270 +           Jmsg(jcr, M_INFO, 0, _("      st_mode  differ. Cat: %x File: %x\n"),
271 +                (uint32_t)statc.st_mode, (uint32_t)statf.st_mode);
272 +           stat = true;
273 +        }
274 +        break;
275 +      case 'n':                /* number of links */
276 +        if (statc.st_nlink != statf.st_nlink) {
277 +           Jmsg(jcr, M_INFO, 0, _("      st_nlink differ. Cat: %d File: %d\n"),
278 +                (uint32_t)statc.st_nlink, (uint32_t)statf.st_nlink);
279 +           stat = true;
280 +        }
281 +        break;
282 +      case 'u':                /* user id */
283 +        if (statc.st_uid != statf.st_uid) {
284 +           Jmsg(jcr, M_INFO, 0, _("      st_uid   differ. Cat: %u File: %u\n"),
285 +                (uint32_t)statc.st_uid, (uint32_t)statf.st_uid);
286 +           stat = true;
287 +        }
288 +        break;
289 +      case 'g':                /* group id */
290 +        if (statc.st_gid != statf.st_gid) {
291 +           Jmsg(jcr, M_INFO, 0, _("      st_gid   differ. Cat: %u File: %u\n"),
292 +                (uint32_t)statc.st_gid, (uint32_t)statf.st_gid);
293 +           stat = true;
294 +        }
295 +        break;
296 +      case 's':                /* size */
297 +        if (statc.st_size != statf.st_size) {
298 +           Jmsg(jcr, M_INFO, 0, _("      st_size  differ. Cat: %s File: %s\n"),
299 +                edit_uint64((uint64_t)statc.st_size, ed1),
300 +                edit_uint64((uint64_t)statf.st_size, ed2));
301 +           stat = true;
302 +        }
303 +        break;
304 +      case 'a':                /* access time */
305 +        if (statc.st_atime != statf.st_atime) {
306 +           Jmsg(jcr, M_INFO, 0, _("      st_atime differs\n"));
307 +           stat = true;
308 +        }
309 +        break;
310 +      case 'm':
311 +        if (statc.st_mtime != statf.st_mtime) {
312 +           Jmsg(jcr, M_INFO, 0, _("      st_mtime differs\n"));
313 +           stat = true;
314 +        }
315 +        break;
316 +      case 'c':                /* ctime */
317 +        if (statc.st_ctime != statf.st_ctime) {
318 +           Jmsg(jcr, M_INFO, 0, _("      st_ctime differs\n"));
319 +           stat = true;
320 +        }
321 +        break;
322 +      case 'd':                /* file size decrease */
323 +        if (statc.st_size > statf.st_size) {
324 +           Jmsg(jcr, M_INFO, 0, _("      st_size  decrease. Cat: %s File: %s\n"),
325 +                edit_uint64((uint64_t)statc.st_size, ed1),
326 +                edit_uint64((uint64_t)statf.st_size, ed2));
327 +           stat = true;
328 +        }
329 +        break;
330 +      case '5':                /* compare MD5 */
331 +        Dmsg1(500, "set Do_MD5 for %s\n", jcr->fname);
332 +//      *do_Digest = CRYPTO_DIGEST_MD5;
333 +        break;
334 +      case '1':                 /* compare SHA1 */
335 +//      *do_Digest = CRYPTO_DIGEST_SHA1;
336 +        break;
337 +      case ':':
338 +      case 'V':
339 +      default:
340 +        break;
341 +      }
342 +   }
343 +   *lstat = '\0';              /* mark it as deleted */
344 +   return stat;
345 +}
346 +
347 +#include "lib/htable.c"
348 +int accurate_get_current_file_list_cmd(JCR *jcr)
349 +{
350 +   BSOCK *dir = jcr->dir_bsock;
351 +   char *lstat;
352 +   int len;
353 +   uint64_t nb;
354 +
355 +   if (jcr->accurate == false || job_canceled(jcr)) {
356 +      return true;
357 +   }
358 +
359 +   if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
360 +      dir->fsend(_("2991 Bad accurate command\n"));
361 +      return false;
362 +   }
363 +
364 +   jcr->file_list = (htable *)malloc(sizeof(htable));
365 +   jcr->file_list->init(jcr, &jcr->link, nb);
366 +
367 +   /* get current files */
368 +   while (dir->recv() >= 0) {
369 +      len = strlen(dir->msg);
370 +      if ((len+1) < dir->msglen) {
371 +        char *elt = (char *) malloc(dir->msglen+1);
372 +        memcpy(elt, dir->msg, dir->msglen+1);
373 +        lstat = elt + len + 1;
374 +        Dmsg2(5, "hash[%s]=%s\n", elt, lstat);
375 +        jcr->file_list->insert(elt, lstat);
376 +      }
377 +   }
378 +  
379 +   jcr->file_list->stats();
380 +//   dir->fsend("2000 OK accurate\n");
381 +
382 +   return true;
383 +}
384 +
385 +/*
386   * Find all the requested files and send them
387   * to the Storage daemon.
388   *
389 @@ -66,7 +233,6 @@
390     BSOCK *sd;
391     bool ok = true;
392     // TODO landonf: Allow user to specify encryption algorithm
393 -
394     sd = jcr->store_bsock;
395  
396     set_jcr_job_status(jcr, JS_Running);
397 @@ -134,7 +300,12 @@
398        ok = false;                     /* error */
399        set_jcr_job_status(jcr, JS_ErrorTerminated);
400     }
401 +   Dmsg1(1, "jcr->accurate == %i\n", jcr->accurate);
402  
403 +   if (jcr->accurate) {
404 +      //accurate_send_deleted_list(jcr);              /* send deleted list to SD  */
405 +   }
406 +
407     free_pool_memory(jcr->acl_text);
408  
409     stop_heartbeat_monitor(jcr);
410 @@ -355,9 +526,11 @@
411     case FT_DIRNOCHG:
412     case FT_NOCHG:
413        Jmsg(jcr, M_SKIPPED, 1, _("     Unchanged file skipped: %s\n"), ff_pkt->fname);
414 +      accurate_check_file(jcr, ff_pkt, NULL); /* list skipped files */
415        return 1;
416     case FT_ISARCH:
417        Jmsg(jcr, M_NOTSAVED, 0, _("     Archive file not saved: %s\n"), ff_pkt->fname);
418 +      accurate_check_file(jcr, ff_pkt, NULL); /* list skipped files */
419        return 1;
420     case FT_NOOPEN: {
421        berrno be;
422 @@ -1118,6 +1291,9 @@
423     }
424     unstrip_path(ff_pkt);
425  
426 +   /* list backuped files */
427 +   accurate_check_file(jcr, ff_pkt, attribs);
428 +
429     Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
430     if (!stat) {
431        Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
432 @@ -1128,6 +1304,58 @@
433     return true;
434  }
435  
436 +static bool encode_and_send_deleted_file(JCR *jcr, char *fname) 
437 +{
438 +   BSOCK *sd = jcr->store_bsock;
439 +   char *attribs;
440 +   char *attribsEx;
441 +   int stat;
442 +#ifdef FD_NO_SEND_TEST
443 +   return true;
444 +#endif
445 +
446 +   attribs = " ";
447 +   attribsEx = " ";
448 +
449 +   /*
450 +    * Send Attributes header to Storage daemon
451 +    *    <file-index> <stream> <info>
452 +    */
453 +   if (!sd->fsend("%ld %d 0", 0, STREAM_UNIX_ATTRIBUTES)) {
454 +      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
455 +            sd->bstrerror());
456 +      return false;
457 +   }
458 +   Dmsg1(300, ">stored: attrhdr %s\n", sd->msg);
459 +
460 +   /*
461 +    * Send file attributes to Storage daemon
462 +    *   File_index
463 +    *   File type
464 +    *   Filename (full path)
465 +    *   Encoded attributes
466 +    *   Link name (if type==FT_LNK or FT_LNKSAVED)
467 +    *   Encoded extended-attributes (for Win32)
468 +    *
469 +    * For a directory, link is the same as fname, but with trailing
470 +    * slash. For a linked file, link is the link.
471 +    */
472 +   stat = sd->fsend("%ld %d %s%c%s%c%s%c%s%c", 
473 +                   0 /* FileIndex */,
474 +                   FT_NOSTAT /* FileType */,
475 +                   fname /* FileName */, 
476 +                   0, attribs, 0, 0, 0, attribsEx, 0);
477 +
478 +   Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
479 +   if (!stat) {
480 +      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
481 +            sd->bstrerror());
482 +      return false;
483 +   }
484 +   sd->signal(BNET_EOD);            /* indicate end of attributes data */
485 +   return true;
486 +}
487 +
488  /* 
489   * Do in place strip of path
490   */
491 Index: src/filed/job.c
492 ===================================================================
493 --- src/filed/job.c     (révision 6443)
494 +++ src/filed/job.c     (copie de travail)
495 @@ -49,6 +49,7 @@
496  /* Imported functions */
497  extern int status_cmd(JCR *jcr);
498  extern int qstatus_cmd(JCR *jcr);
499 +extern int accurate_get_current_file_list_cmd(JCR *jcr);
500  
501  /* Forward referenced functions */
502  static int backup_cmd(JCR *jcr);
503 @@ -106,6 +107,7 @@
504     {"RunBeforeJob", runbefore_cmd, 0},
505     {"RunAfterJob",  runafter_cmd,  0},
506     {"Run",          runscript_cmd, 0},
507 +   {"accurate",     accurate_get_current_file_list_cmd, 0},
508     {NULL,       NULL}                  /* list terminator */
509  };
510  
511 @@ -1087,6 +1089,9 @@
512        case 'c':
513           fo->flags |= FO_CHKCHANGES;
514           break;
515 +      case 'C':
516 +         fo->flags |= FO_ACCURATE;
517 +         break;
518        default:
519           Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
520           break;
521 @@ -1195,6 +1200,9 @@
522  
523     level = get_memory(dir->msglen+1);
524     Dmsg1(110, "level_cmd: %s", dir->msg);
525 +   if (strstr(dir->msg, "accurate")) {
526 +      jcr->accurate = true;
527 +   }
528     if (sscanf(dir->msg, "level = %s ", level) != 1) {
529        goto bail_out;
530     }
531 @@ -1204,14 +1212,14 @@
532     /* Full backup requested? */
533     } else if (strcmp(level, "full") == 0) {
534        jcr->JobLevel = L_FULL;
535 -   } else if (strcmp(level, "differential") == 0) {
536 +   } else if (strstr(level, "differential")) {
537        jcr->JobLevel = L_DIFFERENTIAL;
538        free_memory(level);
539        return 1;
540 -   } else if (strcmp(level, "incremental") == 0) {
541 +   } else if (strstr(level, "incremental")) {
542        jcr->JobLevel = L_INCREMENTAL;
543        free_memory(level);
544 -      return 1;   
545 +      return 1;
546     /*
547      * We get his UTC since time, then sync the clocks and correct it
548      *   to agree with our clock.
549 Index: src/cats/sql_update.c
550 ===================================================================
551 --- src/cats/sql_update.c       (révision 6443)
552 +++ src/cats/sql_update.c       (copie de travail)
553 @@ -88,6 +88,102 @@
554     return stat;
555  }
556  
557 +int db_accurate_delete_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t BackupId)
558 +{
559 +   int stat;
560 +   char ed1[50], ed2[50];
561 +   db_lock(mdb);
562 +   Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE FileId=%s AND BackupId=%s",
563 +       edit_int64(FileId, ed1), edit_int64(BackupId, ed2));
564 +   stat = INSERT_DB(jcr, mdb, mdb->cmd);
565 +   db_unlock(mdb);
566 +   return stat;
567 +}
568 +
569 +int db_accurate_mark_file_for_backup(JCR *jcr, B_DB *mdb, char *fname, JobId_t JobId)
570 +{
571 +   int stat;
572 +   char ed1[50];
573 +   db_lock(mdb);
574 +   /* TODO: mdb->esc_xxx are already ok but it's more smart to recompute it */
575 +//   mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*len+2);
576 +//   mdb->esc_name = db_escape_string(jcr, mdb, mdb->esc_name, fname, len);
577 +   Mmsg(mdb->cmd, "INSERT INTO ToBackup%s (name) VALUES ('%s%s')", edit_int64(JobId, ed1), mdb->esc_path, mdb->esc_name);
578 +   stat = INSERT_DB(jcr, mdb, mdb->cmd);
579 +   db_unlock(mdb);
580 +   return stat;
581 +}
582 +
583 +int db_accurate_cleanup_currentfile(JCR *jcr, B_DB *mdb, JobId_t BackupId)
584 +{
585 +   int stat;
586 +   char ed1[50];
587 +   db_lock(mdb);
588 +   Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE BackupId=%s", edit_int64(BackupId, ed1));
589 +   stat = QUERY_DB(jcr, mdb, mdb->cmd);
590 +   db_unlock(mdb);
591 +   return stat;
592 +}
593 +
594 +int db_accurate_update_currentfile(JCR *jcr, B_DB *mdb, JobId_t JobId, int JobLevel, JobId_t BackupId)
595 +{
596 +   int stat;
597 +   char ed1[50], ed2[50], ed3[50];
598 +   db_lock(mdb);
599 +   edit_int64(JobId, ed2);
600 +   Mmsg(mdb->cmd, 
601 +       "INSERT INTO CurrentFile (FileId, BackupId, FullMark, MarkId) "
602 +       " (SELECT FileId, %s, '%c', %s FROM File WHERE JobId=%s)", 
603 +       edit_int64(BackupId, ed1),
604 +       JobLevel, ed2, ed2);
605 +   stat = QUERY_DB(jcr, mdb, mdb->cmd);
606 +   db_unlock(mdb);
607 +   return stat; 
608 +}
609 +
610 +int db_accurate_create_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId)
611 +{
612 +   int stat;
613 +   char ed1[50];
614 +   db_lock(mdb);
615 +   Mmsg(mdb->cmd, "CREATE TABLE ToBackup%s (name text)", edit_int64(JobId, ed1));
616 +//   Mmsg(mdb->cmd, "CREATE TEMPORARY TABLE ToBackup%s (name text)", edit_int64(JobId, ed1));
617 +   stat = QUERY_DB(jcr, mdb, mdb->cmd);
618 +   db_unlock(mdb);
619 +   return stat;
620 +}
621 +
622 +int db_accurate_drop_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId)
623 +{
624 +   int stat=0;
625 +   char ed1[50];
626 +   db_lock(mdb);
627 +//   Mmsg(mdb->cmd, "DROP TABLE ToBackup%s", edit_int64(JobId, ed1));
628 +//   stat = QUERY_DB(jcr, mdb, mdb->cmd);
629 +   db_unlock(mdb);
630 +   return stat;
631 +}
632 +
633 +
634 +/* Mark the file record as being visited during database
635 + * accurate compare. Stuff JobId into the MarkId field
636 + */
637 +int db_accurate_mark_file_record(JCR *jcr, B_DB *mdb, JobId_t BackupId, FileId_t FileId, JobId_t JobId)
638 +{
639 +   int stat;
640 +   char ed1[50], ed2[50], ed3[50];
641 +
642 +   db_lock(mdb);
643 +   Mmsg(mdb->cmd, "UPDATE CurrentFile SET MarkId=%s WHERE FileId=%s AND BackupId=%s", 
644 +       edit_int64(JobId, ed1), edit_int64(FileId, ed2), edit_int64(BackupId, ed3));
645 +   stat = QUERY_DB(jcr, mdb, mdb->cmd);
646 +   if (!stat || sql_affected_rows(mdb) != 1) {
647 +      stat = 0;
648 +   }
649 +   db_unlock(mdb);
650 +   return stat;
651 +}
652 +
653  /*
654   * Update the Job record at start of Job
655   *
656 Index: src/cats/drop_postgresql_tables.in
657 ===================================================================
658 --- src/cats/drop_postgresql_tables.in  (révision 6443)
659 +++ src/cats/drop_postgresql_tables.in  (copie de travail)
660 @@ -5,7 +5,7 @@
661  bindir=@SQL_BINDIR@
662  db_name=@db_name@
663  
664 -$bindir/psql -f - -d ${db_name} $* <<END-OF-DATA
665 +$bindir/psql -f - -U regress -d ${db_name} $* <<END-OF-DATA
666  drop table unsavedfiles;
667  drop table basefiles;
668  drop table jobmedia;
669 Index: src/cats/make_postgresql_tables.in
670 ===================================================================
671 --- src/cats/make_postgresql_tables.in  (révision 6443)
672 +++ src/cats/make_postgresql_tables.in  (copie de travail)
673 @@ -5,7 +5,7 @@
674  bindir=@SQL_BINDIR@
675  db_name=@db_name@
676  
677 -$bindir/psql -f - -d ${db_name} $* <<END-OF-DATA
678 +$bindir/psql -f - -U regress -d ${db_name} $* <<END-OF-DATA
679  
680  CREATE TABLE filename
681  (
682 @@ -43,6 +43,59 @@
683  CREATE INDEX file_jobid_idx on file (jobid);
684  CREATE INDEX file_fp_idx on file (filenameid, pathid);
685  
686 +CREATE TABLE CurrentBackupId
687 +(
688 +     BackupId          serial     not null,
689 +     ClientId          integer    not null,
690 +     JobName           text       not null,
691 +     FileSetId         integer    not null,
692 +     primary key (BackupId)
693 +);
694 +
695 +-- Serait bien de prendre la meme table pour
696 +-- les File et le CurrentBackup...
697 +-- Mais y'a des problemes pour les prunes
698 +
699 +CREATE TABLE CurrentFile
700 +(
701 +     FileId           integer    not null,
702 +     BackupId         integer    not null,
703 +     FullMark         char(1)    default 0,
704 +     MarkId           integer    default 0,
705 +     primary key (FileId)
706 +);
707 +
708 +CREATE INDEX currentfile_fileid on CurrentFile (BackupId);
709 +
710 +-- CREATE TEMPORARY TABLE batch (fileindex int,
711 +--                               jobid int,
712 +--                               path varchar,
713 +--                               name varchar,
714 +--                               lstat varchar,
715 +--                               md5 varchar);
716 +-- 
717 +-- -- On batch insert dans la table temporaire
718 +
719 +-- il faut trouver les fichiers manquant
720 +-- INSERT des nouveaux, UPDATE des anciens, SELECT pour trouver les deletes
721 +
722 +
723 +-- il faut trouver les fichiers modifies
724 +-- Le champs LStat n'est plus le meme
725 +-- SELECT * 
726 +--   FROM CurrentBackup, 
727 +--        batch JOIN Path USING (Path) JOIN Filename USING (Name)
728 +--  WHERE Path.PathId = CurrentBackup.PathId
729 +--    AND Filename.FilenameId = CurrentBackup.FilenameId
730 +--    AND CurrentBackup.LStat != batch.LStat
731 +-- 
732 +-- il faut mettre a jour la liste des fichiers
733 +
734 +
735 +
736 +
737 +
738 +
739  --
740  -- Possibly add one or more of the following indexes
741  --  if your Verifies are too slow.
742 Index: src/cats/protos.h
743 ===================================================================
744 --- src/cats/protos.h   (révision 6443)
745 +++ src/cats/protos.h   (copie de travail)
746 @@ -78,14 +78,17 @@
747  /* sql_delete.c */
748  int db_delete_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pool_dbr);
749  int db_delete_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr);
750 +int db_accurate_clean_deleted_files(JCR *jcr, B_DB *mdb, JobId_t JobId, JobId_t BackupId);
751  
752  /* sql_find.c */
753  bool db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime);
754  bool db_find_last_jobid(JCR *jcr, B_DB *mdb, const char *Name, JOB_DBR *jr);
755 +JobId_t db_accurate_find_backupid(JCR *jcr, B_DB *mdb, JOB_DBR *jr);
756  int db_find_next_volume(JCR *jcr, B_DB *mdb, int index, bool InChanger, MEDIA_DBR *mr);
757  bool db_find_failed_job_since(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM *stime, int &JobLevel);
758  
759  /* sql_get.c */
760 +int db_accurate_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JobId_t backupid, FILE_DBR *fdbr);
761  bool db_get_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pdbr);
762  int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cr);
763  bool db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr);
764 @@ -129,6 +132,14 @@
765  int  db_update_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr);
766  int  db_add_digest_to_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, char *digest, int type);
767  int  db_mark_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t JobId);
768 +int db_accurate_mark_file_for_backup(JCR *jcr, B_DB *mdb, char *fname, FileId_t JobId);
769 +int db_accurate_mark_file_record(JCR *jcr, B_DB *mdb, JobId_t BackupId, FileId_t FileId, JobId_t JobId);
770 +int db_accurate_drop_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId);
771 +int db_accurate_create_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId);
772 +int db_accurate_delete_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t BackupId);
773  void db_make_inchanger_unique(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr);
774 +int db_accurate_cleanup_currentfile(JCR *jcr, B_DB *mdb, JobId_t BackupId);
775 +int db_accurate_update_currentfile(JCR *jcr, B_DB *mdb, JobId_t JobId, int JobLevel, JobId_t BackupId);
776  
777 +
778  #endif /* __SQL_PROTOS_H */
779 Index: src/cats/sql_find.c
780 ===================================================================
781 --- src/cats/sql_find.c (révision 6443)
782 +++ src/cats/sql_find.c (copie de travail)
783 @@ -190,7 +190,6 @@
784     return true;
785  }
786  
787 -
788  /*
789   * Find JobId of last job that ran.  E.g. for
790   *   VERIFY_CATALOG we want the JobId of the last INIT.
791 Index: src/cats/sql_delete.c
792 ===================================================================
793 --- src/cats/sql_delete.c       (révision 6443)
794 +++ src/cats/sql_delete.c       (copie de travail)
795 @@ -236,5 +236,22 @@
796     return 1;
797  }
798  
799 +/*
800 + * Purge delete file from CurrentFile table. This table contains only
801 + * current files.
802 + */
803 +int db_accurate_clean_deleted_files(JCR *jcr, B_DB *mdb, JobId_t JobId, JobId_t BackupId)
804 +{
805 +   int stat;
806 +   char ed1[50], ed2[50];
807 +   db_lock(mdb);
808 +   Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE MarkId!=%s AND BackupId=%s", 
809 +       edit_int64(JobId, ed1), edit_int64(BackupId, ed2));
810 +   stat = QUERY_DB(jcr, mdb, mdb->cmd);
811 +   db_unlock(mdb);
812 +   return stat;
813  
814 +}
815 +
816 +
817  #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/
818 Index: src/cats/sql_create.c
819 ===================================================================
820 --- src/cats/sql_create.c       (révision 6443)
821 +++ src/cats/sql_create.c       (copie de travail)
822 @@ -829,6 +829,14 @@
823     return true;
824  }
825  
826 +bool db_accurate_insert(JCR *jcr, B_DB *mdb, bool saved, const char *fname, struct stat *stat)
827 +{
828 +   int len;
829 +   split_path_and_file(jcr, mdb, fname);
830 +   /* make like in Verify code */
831 +   return true;
832 +} 
833 +
834  /*
835   * Create File record in B_DB
836   *
837 Index: src/cats/sql_get.c
838 ===================================================================
839 --- src/cats/sql_get.c  (révision 6443)
840 +++ src/cats/sql_get.c  (copie de travail)
841 @@ -66,6 +66,8 @@
842   *
843   *  Returns: 0 on failure
844   *           1 on success with the File record in FILE_DBR
845 + *
846 + * TODO: optimize this with only one query
847   */
848  int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JOB_DBR *jr, FILE_DBR *fdbr)
849  {
850 @@ -86,7 +88,6 @@
851     return stat;
852  }
853  
854 -
855  /*
856   * Get a File record
857   * Returns: 0 on failure
858 Index: src/stored/append.c
859 ===================================================================
860 --- src/stored/append.c (révision 6443)
861 +++ src/stored/append.c (copie de travail)
862 @@ -146,7 +146,7 @@
863  
864        /* Read Stream header from the File daemon.
865         *  The stream header consists of the following:
866 -       *    file_index (sequential Bacula file index, base 1)
867 +       *    file_index (sequential Bacula file index, base 1, 0 for deleted files)
868         *    stream     (Bacula number to distinguish parts of data)
869         *    info       (Info for Storage daemon -- compressed, encryped, ...)
870         *       info is not currently used, so is read, but ignored!
871 @@ -185,16 +185,18 @@
872  
873        Dmsg2(890, "<filed: Header FilInx=%d stream=%d\n", file_index, stream);
874  
875 -      if (!(file_index > 0 && (file_index == last_file_index ||
876 -          file_index == last_file_index + 1))) {
877 -         Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
878 -         ok = false;
879 -         break;
880 +      if (file_index != 0) {   /* TODO: handle file_index == 0 */
881 +        if (!(file_index > 0 && (file_index == last_file_index ||
882 +                                 file_index == last_file_index + 1))) {
883 +           Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
884 +           ok = false;
885 +           break;
886 +        }
887 +        if (file_index != last_file_index) {
888 +           jcr->JobFiles = file_index;
889 +           last_file_index = file_index;
890 +        }
891        }
892 -      if (file_index != last_file_index) {
893 -         jcr->JobFiles = file_index;
894 -         last_file_index = file_index;
895 -      }
896  
897        /* Read data stream from the File daemon.
898         *  The data stream is just raw bytes
899 @@ -212,25 +214,26 @@
900              stream_to_ascii(buf1, rec.Stream,rec.FileIndex),
901              rec.data_len);
902  
903 -         while (!write_record_to_block(dcr->block, &rec)) {
904 -            Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
905 -                       rec.remainder);
906 -            if (!write_block_to_device(dcr)) {
907 -               Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
908 -                  dev->print_name(), dev->bstrerror());
909 -               ok = false;
910 -               break;
911 -            }
912 -         }
913 -         if (!ok) {
914 -            Dmsg0(400, "Not OK\n");
915 -            break;
916 -         }
917 -         jcr->JobBytes += rec.data_len;   /* increment bytes this job */
918 -         Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
919 -            FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
920 -            stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
921 +        while (!write_record_to_block(dcr->block, &rec)) {
922 +           Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
923 +                 rec.remainder);
924 +           if (!write_block_to_device(dcr)) {
925 +              Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
926 +                    dev->print_name(), dev->bstrerror());
927 +              ok = false;
928 +              break;
929 +           }
930  
931 +           if (!ok) {
932 +              Dmsg0(400, "Not OK\n");
933 +              break;
934 +           }
935 +           jcr->JobBytes += rec.data_len;   /* increment bytes this job */
936 +           Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
937 +                 FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
938 +                 stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
939 +        }
940 +
941           /* Send attributes and digest to Director for Catalog */
942           if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX ||
943               crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
944 Index: src/jcr.h
945 ===================================================================
946 --- src/jcr.h   (révision 6443)
947 +++ src/jcr.h   (copie de travail)
948 @@ -119,6 +119,7 @@
949  
950  /* Forward referenced structures */
951  class JCR;
952 +class htable;
953  struct FF_PKT;
954  struct B_DB;
955  struct ATTR_DBR;
956 @@ -318,6 +319,7 @@
957     CRYPTO_CTX crypto;                 /* Crypto ctx */
958     DIRRES* director;                  /* Director resource */
959     bool VSS;                          /* VSS used by FD */
960 +   htable *file_list;                 /* Previous file list (accurate mode) */
961  #endif /* FILE_DAEMON */
962  
963  
964 Index: src/findlib/find.h
965 ===================================================================
966 --- src/findlib/find.h  (révision 6443)
967 +++ src/findlib/find.h  (copie de travail)
968 @@ -108,6 +108,7 @@
969  #define FO_ENHANCEDWILD (1<<23)       /* Enhanced wild card processing */
970  #define FO_CHKCHANGES   (1<<24)       /* Check if file have been modified during backup */
971  #define FO_STRIPPATH    (1<<25)       /* Check for stripping path */
972 +#define FO_ACCURATE     (1<<26)       /* Accurate mode */
973  
974  struct s_included_file {
975     struct s_included_file *next;