]> git.sur5r.net Git - bacula/bacula/blob - bacula/patches/testing/project-accurate-backup.patch2
ebl update
[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 @@ -96,6 +96,140 @@
58     return true;
59  }
60  
61 +static int get_int_handler(void *ctx, int num_fields, char **row)
62 +{
63 +   POOLMEM *ret = (POOLMEM *)ctx;
64 +   if (num_fields == 1) {
65 +      if (ret[0] != 0) {
66 +        pm_strcat(ret, ",");
67 +      }
68 +      pm_strcat(ret, row[0]);
69 +   }
70 +   return 0;
71 +}
72 +
73 +static int accurate_list_handler(void *ctx, int num_fields, char **row)
74 +{
75 +   JCR *jcr = (JCR *)ctx;
76 +
77 +   if (job_canceled(jcr)) {
78 +      return 1;
79 +   }
80 +   
81 +   if (row[2] > 0) {           /* discard when file_index == 0 */
82 +      jcr->file_bsock->fsend("%s%s%c%s", row[0], row[1], 0, row[4]); 
83 +   }
84 +   return 0;
85 +}
86 +
87 +/* Full : do nothing
88 + * Differential : get the last full id
89 + * Incremental : get the last full + last diff + last incr(s) ids
90 + *
91 + * TODO: look and merge from ua_restore.c
92 + */
93 +bool db_accurate_get_jobids(JCR *jcr, POOLMEM *jobids)
94 +{
95 +   char clientid[50], jobid[50], filesetid[50];
96 +   char date[MAX_TIME_LENGTH];
97 +
98 +   JOB_DBR *jr = &jcr->jr; 
99 +   POOLMEM *query = get_pool_memory(PM_FNAME);
100 +   bstrutime(date, sizeof(date),  time(NULL) + 1);
101 +
102 +   Mmsg(query, 
103 +"CREATE TEMPORARY TABLE btemp3%s AS ( "
104 + "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
105 +   "FROM Job JOIN FileSet USING (FileSetId) "
106 +  "WHERE ClientId = %s "
107 +    "AND Level='F' AND JobStatus='T' AND Type='B' "
108 +    "AND StartTime<'%s' "
109 +    "AND FileSet.FileSet=(SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
110 +  "ORDER BY Job.JobTDate DESC LIMIT 1) ",
111 +       edit_uint64(jcr->JobId, jobid),
112 +       edit_uint64(jr->ClientId, clientid),
113 +       date,
114 +       edit_uint64(jr->FileSetId, filesetid));
115 +   db_sql_query(jcr->db, query, NULL, NULL);
116 +
117 +   if (jcr->JobLevel == L_INCREMENTAL) {
118 +
119 +      Mmsg(query, 
120 +"INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
121 + "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
122 +   "FROM Job JOIN FileSet USING (FileSetId) "
123 +  "WHERE ClientId = %s "
124 +    "AND Level='D' AND JobStatus='T' AND Type='B' "
125 +    "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
126 +    "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
127 +  "ORDER BY Job.JobTDate DESC LIMIT 1 ",
128 +          jobid,
129 +          clientid,
130 +          jobid,
131 +          filesetid);
132 +      db_sql_query(jcr->db, query, NULL, NULL);
133 +
134 +      Mmsg(query, 
135 +"INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
136 + "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
137 +   "FROM Job JOIN FileSet USING (FileSetId) "
138 +  "WHERE ClientId = %s "
139 +    "AND Level='I' AND JobStatus='T' AND Type='B' "
140 +    "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
141 +    "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
142 +  "ORDER BY Job.JobTDate DESC ",
143 +          jobid,
144 +          clientid,
145 +          jobid,
146 +          filesetid);
147 +      db_sql_query(jcr->db, query, NULL, NULL);
148 +   }
149 +
150 +   jobids[0]='\0';
151 +   Mmsg(query, "SELECT JobId FROM btemp3%s", jobid);
152 +   db_sql_query(jcr->db, query, get_int_handler, jobids);
153 +   Dmsg1(1, "db_accurate_get_jobids=%s\n", jobids);
154 +
155 +   Mmsg(query, "DROP TABLE btemp3%s", jobid);
156 +   db_sql_query(jcr->db, query, NULL, NULL);
157 +   free_pool_memory(query);
158 +
159 +   return 1;
160 +}
161 +
162 +bool send_accurate_current_files(JCR *jcr)
163 +{
164 +   char buf[MAXSTRING];
165 +
166 +   if (jcr->accurate == false || job_canceled(jcr) || jcr->JobLevel == L_FULL) {
167 +      return true;
168 +   }
169 +   POOLMEM *jobids = get_pool_memory(PM_FNAME);
170 +   db_accurate_get_jobids(jcr, jobids);
171 +
172 +   if (*jobids == 0) {
173 +      free_pool_memory(jobids);
174 +      Jmsg(jcr, M_ERROR_TERM, 0, _("Cannot find previous jobids.\n"));
175 +      return true;
176 +   }
177 +
178 +   /* to be able to allocate the right size for htable */
179 +   POOLMEM *nb = get_pool_memory(PM_FNAME);
180 +   bsnprintf(buf, sizeof(buf), "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)",jobids);
181 +   db_sql_query(jcr->db, buf, get_int_handler, nb);
182 +   jcr->file_bsock->fsend("accurate files=%s\n", nb); 
183 +
184 +   db_get_file_list(jcr->db, jobids, accurate_list_handler, (void *)jcr);
185 +
186 +   free_pool_memory(jobids);
187 +   free_pool_memory(nb);
188 +
189 +   jcr->file_bsock->signal(BNET_EOD);
190 +   /* TODO: use response() ? */
191 +
192 +   return true;
193 +}
194 +
195  /*
196   * Do a backup of the specified FileSet
197   *
198 @@ -225,6 +359,14 @@
199        Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
200     }
201  
202 +   /*
203 +    * If backup is in accurate mode, FD will send the list of
204 +    * all files.
205 +    */
206 +   if (!send_accurate_current_files(jcr)) {
207 +      goto bail_out;
208 +   }
209 +
210     /* Send backup command */
211     fd->fsend(backupcmd);
212     if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
213 @@ -234,6 +376,7 @@
214     /* Pickup Job termination data */
215     stat = wait_for_job_termination(jcr);
216     db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
217 +
218     if (stat == JS_Terminated) {
219        backup_cleanup(jcr, stat);
220        return true;
221 Index: src/dird/ua_restore.c
222 ===================================================================
223 --- src/dird/ua_restore.c       (révision 6443)
224 +++ src/dird/ua_restore.c       (copie de travail)
225 @@ -1006,7 +1006,6 @@
226      * For display purposes, the same JobId, with different volumes may
227      * appear more than once, however, we only insert it once.
228      */
229 -   int items = 0;
230     p = rx->JobIds;
231     tree.FileEstimate = 0;
232     if (get_next_jobid_from_list(&p, &JobId) > 0) {
233 @@ -1021,24 +1020,14 @@
234           tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
235        }
236     }
237 -   for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
238 -      char ed1[50];
239  
240 -      if (JobId == last_JobId) {
241 -         continue;                    /* eliminate duplicate JobIds */
242 -      }
243 -      last_JobId = JobId;
244 -      ua->info_msg(_("\nBuilding directory tree for JobId %s ...  "), 
245 -         edit_int64(JobId, ed1));
246 -      items++;
247 -      /*
248 -       * Find files for this JobId and insert them in the tree
249 -       */
250 -      Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
251 -      if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
252 -         ua->error_msg("%s", db_strerror(ua->db));
253 -      }
254 +   ua->info_msg(_("\nBuilding directory tree for JobId(s) %s ...  "),
255 +               rx->JobIds);
256 +
257 +   if (!db_get_file_list(ua->db, rx->JobIds, insert_tree_handler, (void *)&tree)) {
258 +      ua->error_msg("%s", db_strerror(ua->db));
259     }
260 +
261     if (tree.FileCount == 0) {
262        ua->send_msg(_("\nThere were no files inserted into the tree, so file selection\n"
263           "is not possible.Most likely your retention policy pruned the files\n"));
264 @@ -1056,26 +1045,13 @@
265        }
266     } else {
267        char ec1[50];
268 -      if (items==1) {
269 -         if (tree.all) {
270 -            ua->info_msg(_("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
271 -              edit_uint64_with_commas(tree.FileCount, ec1));
272 -         }
273 -         else {
274 -            ua->info_msg(_("\n1 Job, %s files inserted into the tree.\n"),
275 -              edit_uint64_with_commas(tree.FileCount, ec1));
276 -         }
277 +      if (tree.all) {
278 +        ua->info_msg(_("\n%s files inserted into the tree and marked for extraction.\n"),
279 +                     edit_uint64_with_commas(tree.FileCount, ec1));
280 +      } else {
281 +        ua->info_msg(_("\n%s files inserted into the tree.\n"),
282 +                     edit_uint64_with_commas(tree.FileCount, ec1));
283        }
284 -      else {
285 -         if (tree.all) {
286 -            ua->info_msg(_("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
287 -              items, edit_uint64_with_commas(tree.FileCount, ec1));
288 -         }
289 -         else {
290 -            ua->info_msg(_("\n%d Jobs, %s files inserted into the tree.\n"),
291 -              items, edit_uint64_with_commas(tree.FileCount, ec1));
292 -         }
293 -      }
294  
295        if (find_arg(ua, NT_("done")) < 0) {
296           /* Let the user interact in selecting which files to restore */
297 Index: src/dird/inc_conf.c
298 ===================================================================
299 --- src/dird/inc_conf.c (révision 6443)
300 +++ src/dird/inc_conf.c (copie de travail)
301 @@ -94,6 +94,7 @@
302   * Items that are valid in an Options resource
303   */
304  static RES_ITEM options_items[] = {
305 +   {"accurate",        store_opts,    {0},     0, 0, 0},
306     {"compression",     store_opts,    {0},     0, 0, 0},
307     {"signature",       store_opts,    {0},     0, 0, 0},
308     {"verify",          store_opts,    {0},     0, 0, 0},
309 @@ -153,7 +154,8 @@
310     INC_KW_NOATIME,
311     INC_KW_ENHANCEDWILD,
312     INC_KW_CHKCHANGES,
313 -   INC_KW_STRIPPATH
314 +   INC_KW_STRIPPATH,
315 +   INC_KW_ACCURATE
316  };
317  
318  /*
319 @@ -163,6 +165,7 @@
320   *   options given above.
321   */
322  static struct s_kw FS_option_kw[] = {
323 +   {"accurate",    INC_KW_ACCURATE},
324     {"compression", INC_KW_COMPRESSION},
325     {"signature",   INC_KW_DIGEST},
326     {"encryption",  INC_KW_ENCRYPTION},
327 @@ -251,6 +254,8 @@
328     {"no",       INC_KW_ENHANCEDWILD,  "0"},
329     {"yes",      INC_KW_CHKCHANGES,    "c"},
330     {"no",       INC_KW_CHKCHANGES,    "0"},
331 +   {"yes",      INC_KW_ACCURATE,      "C"},
332 +   {"no",       INC_KW_ACCURATE,      "0"},
333     {NULL,       0,                      0}
334  };
335  
336 Index: src/filed/backup.c
337 ===================================================================
338 --- src/filed/backup.c  (révision 6443)
339 +++ src/filed/backup.c  (copie de travail)
340 @@ -37,6 +37,7 @@
341  
342  #include "bacula.h"
343  #include "filed.h"
344 +#include "lib/htable.h"
345  
346  /* Forward referenced functions */
347  int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
348 @@ -48,8 +49,225 @@
349  static bool crypto_session_start(JCR *jcr);
350  static void crypto_session_end(JCR *jcr);
351  static bool crypto_session_send(JCR *jcr, BSOCK *sd);
352 +static bool encode_and_send_deleted_file(JCR *jcr, char *fname);
353  
354 +typedef struct CurFile {
355 +   char *fname;
356 +   char *lstat;
357 +   hlink link;
358 +} CurFile;
359 +
360  /*
361 + * This function is called for each file seen in fileset.
362 + * 
363 + * If the file is skipped (saved=false), we will check if this
364 + * file have been backuped before. If not, we decide to backup it.
365 + *
366 + * If the file have saved=true, we mark it as seen
367 + * 
368 + */
369 +/* TODO: tweak verify code to use the same function */
370 +bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt, bool saved)
371 +{
372 +   char *p;
373 +   int stat=false;
374 +   struct stat statc;                 /* catalog stat */
375 +   char *Opts_Digest = ff_pkt->VerifyOpts;
376 +   char *fname = ff_pkt->fname;
377 +   CurFile *elt;
378 +
379 +   int32_t LinkFIc;
380 +
381 +   if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
382 +      return true;
383 +   }
384 +
385 +   if (ff_pkt->type == FT_DIREND || ff_pkt->type == FT_REPARSE || ff_pkt->type == FT_DIRNOCHG) {
386 +      fname = ff_pkt->link;
387 +   } 
388 +
389 +   elt = (CurFile *) jcr->file_list->lookup(fname);
390 +
391 +   if (!elt) {
392 +      // TODO: we must backup it !
393 +      Dmsg1(1, "accurate %s = yes (not found)\n", fname);
394 +      return true;
395 +   }
396 +
397 +   if (saved || *elt->lstat == '\0') {
398 +      Dmsg1(1, "accurate %s = no (already seen)\n", fname);
399 +      *elt->lstat = '\0';
400 +      return false;
401 +   }
402 +
403 +   decode_stat(elt->lstat, &statc, &LinkFIc); /* decode catalog stat */
404 +//   *do_Digest = CRYPTO_DIGEST_NONE;
405 +
406 +   for (p=Opts_Digest; *p; p++) {
407 +      char ed1[30], ed2[30];
408 +      switch (*p) {
409 +      case 'i':                /* compare INODEs */
410 +        if (statc.st_ino != ff_pkt->statp.st_ino) {
411 +           Jmsg(jcr, M_INFO, 0, _("      st_ino   differ. Cat: %s File: %s\n"),
412 +                edit_uint64((uint64_t)statc.st_ino, ed1),
413 +                edit_uint64((uint64_t)ff_pkt->statp.st_ino, ed2));
414 +           stat = true;
415 +        }
416 +        break;
417 +      case 'p':                /* permissions bits */
418 +        if (statc.st_mode != ff_pkt->statp.st_mode) {
419 +           Jmsg(jcr, M_INFO, 0, _("      st_mode  differ. Cat: %x File: %x\n"),
420 +                (uint32_t)statc.st_mode, (uint32_t)ff_pkt->statp.st_mode);
421 +           stat = true;
422 +        }
423 +        break;
424 +      case 'n':                /* number of links */
425 +        if (statc.st_nlink != ff_pkt->statp.st_nlink) {
426 +           Jmsg(jcr, M_INFO, 0, _("      st_nlink differ. Cat: %d File: %d\n"),
427 +                (uint32_t)statc.st_nlink, (uint32_t)ff_pkt->statp.st_nlink);
428 +           stat = true;
429 +        }
430 +        break;
431 +      case 'u':                /* user id */
432 +        if (statc.st_uid != ff_pkt->statp.st_uid) {
433 +           Jmsg(jcr, M_INFO, 0, _("      st_uid   differ. Cat: %u File: %u\n"),
434 +                (uint32_t)statc.st_uid, (uint32_t)ff_pkt->statp.st_uid);
435 +           stat = true;
436 +        }
437 +        break;
438 +      case 'g':                /* group id */
439 +        if (statc.st_gid != ff_pkt->statp.st_gid) {
440 +           Jmsg(jcr, M_INFO, 0, _("      st_gid   differ. Cat: %u File: %u\n"),
441 +                (uint32_t)statc.st_gid, (uint32_t)ff_pkt->statp.st_gid);
442 +           stat = true;
443 +        }
444 +        break;
445 +      case 's':                /* size */
446 +        if (statc.st_size != ff_pkt->statp.st_size) {
447 +           Jmsg(jcr, M_INFO, 0, _("      st_size  differ. Cat: %s File: %s\n"),
448 +                edit_uint64((uint64_t)statc.st_size, ed1),
449 +                edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
450 +           stat = true;
451 +        }
452 +        break;
453 +      case 'a':                /* access time */
454 +        if (statc.st_atime != ff_pkt->statp.st_atime) {
455 +           Jmsg(jcr, M_INFO, 0, _("      st_atime differs\n"));
456 +           stat = true;
457 +        }
458 +        break;
459 +      case 'm':
460 +        if (statc.st_mtime != ff_pkt->statp.st_mtime) {
461 +           Jmsg(jcr, M_INFO, 0, _("      st_mtime differs\n"));
462 +           stat = true;
463 +        }
464 +        break;
465 +      case 'c':                /* ctime */
466 +        if (statc.st_ctime != ff_pkt->statp.st_ctime) {
467 +           Jmsg(jcr, M_INFO, 0, _("      st_ctime differs\n"));
468 +           stat = true;
469 +        }
470 +        break;
471 +      case 'd':                /* file size decrease */
472 +        if (statc.st_size > ff_pkt->statp.st_size) {
473 +           Jmsg(jcr, M_INFO, 0, _("      st_size  decrease. Cat: %s File: %s\n"),
474 +                edit_uint64((uint64_t)statc.st_size, ed1),
475 +                edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
476 +           stat = true;
477 +        }
478 +        break;
479 +      case '5':                /* compare MD5 */
480 +        Dmsg1(500, "set Do_MD5 for %s\n", ff_pkt->fname);
481 +//      *do_Digest = CRYPTO_DIGEST_MD5;
482 +        break;
483 +      case '1':                 /* compare SHA1 */
484 +//      *do_Digest = CRYPTO_DIGEST_SHA1;
485 +        break;
486 +      case ':':
487 +      case 'V':
488 +      default:
489 +        break;
490 +      }
491 +   }
492 +   *elt->lstat = '\0';         /* mark it as seen */
493 +   Dmsg2(1, "accurate %s = %i\n", fname, stat);
494 +   return stat;
495 +}
496 +
497 +/* 
498 + * This function doesn't work very well with smartalloc
499 + */
500 +int accurate_get_current_file_list_cmd(JCR *jcr)
501 +{
502 +   BSOCK *dir = jcr->dir_bsock;
503 +   int len;
504 +   uint64_t nb;
505 +   CurFile *elt=NULL;
506 +
507 +   if (jcr->accurate == false || job_canceled(jcr) || jcr->JobLevel == L_FULL) {
508 +      return true;
509 +   }
510 +
511 +   if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
512 +      dir->fsend(_("2991 Bad accurate command\n"));
513 +      return false;
514 +   }
515 +
516 +   Jmsg(jcr, M_INFO, 0, _("Getting Accurate informations.\n"));
517 +   
518 +   jcr->file_list = (htable *)malloc(sizeof(htable));
519 +   jcr->file_list->init(elt, &elt->link, nb);
520 +
521 +   /* get current files */
522 +   while (dir->recv() >= 0) {
523 +      len = strlen(dir->msg);
524 +      if ((len+1) < dir->msglen) {
525 +//      elt = (CurFile *)malloc(sizeof(CurFile));
526 +//      elt->fname  = (char *) malloc(dir->msglen+1);
527 +
528 +        /* we store CurFile, fname and lstat in the same chunk */
529 +        elt = (CurFile *)malloc(sizeof(CurFile)+dir->msglen+1);
530 +        elt->fname  = (char *) elt+sizeof(CurFile);
531 +        memcpy(elt->fname, dir->msg, dir->msglen);
532 +        elt->fname[dir->msglen]='\0';
533 +        elt->lstat = elt->fname + len + 1;
534 +        jcr->file_list->insert(elt->fname, elt); 
535 +        Dmsg2(1, "add fname=%s lstat=%s\n", elt->fname, elt->lstat);
536 +      }
537 +   }
538 +
539 +//   jcr->file_list->stats();
540 +   /* TODO: send a EOM ?
541 +   dir->fsend("2000 OK accurate\n");
542 +    */
543 +   return true;
544 +}
545 +
546 +bool accurate_send_deleted_list(JCR *jcr)
547 +{
548 +   if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
549 +      return true;
550 +   }
551 +
552 +   if (jcr->file_list == NULL) { /* TODO: bug ? */
553 +      return true;
554 +   }
555 +
556 +   CurFile *elt;
557 +   foreach_htable (elt, jcr->file_list) {
558 +      if (*elt->lstat != '\0') {
559 +        Dmsg2(1, "deleted fname=%s lstat=%s\n", elt->fname, elt->lstat);
560 +        encode_and_send_deleted_file(jcr, elt->fname);
561 +      }
562 +//      free(elt->fname);
563 +   }
564 +   jcr->file_list->destroy();  /* TODO: clean htable when this function is not reached ? */
565 +   free(jcr->file_list);
566 +   jcr->file_list = NULL;
567 +   return true;
568 +}
569 +
570 +/*
571   * Find all the requested files and send them
572   * to the Storage daemon.
573   *
574 @@ -66,7 +284,6 @@
575     BSOCK *sd;
576     bool ok = true;
577     // TODO landonf: Allow user to specify encryption algorithm
578 -
579     sd = jcr->store_bsock;
580  
581     set_jcr_job_status(jcr, JS_Running);
582 @@ -100,7 +317,7 @@
583      */
584     jcr->compress_buf_size = jcr->buf_size + ((jcr->buf_size+999) / 1000) + 30;
585     jcr->compress_buf = get_memory(jcr->compress_buf_size);
586 -
587 +   
588  #ifdef HAVE_LIBZ
589     z_stream *pZlibStream = (z_stream*)malloc(sizeof(z_stream));  
590     if (pZlibStream) {
591 @@ -134,7 +351,10 @@
592        ok = false;                     /* error */
593        set_jcr_job_status(jcr, JS_ErrorTerminated);
594     }
595 +   Dmsg1(1, "jcr->accurate == %i\n", jcr->accurate);
596  
597 +   accurate_send_deleted_list(jcr);              /* send deleted list to SD  */
598 +
599     free_pool_memory(jcr->acl_text);
600  
601     stop_heartbeat_monitor(jcr);
602 @@ -354,9 +574,21 @@
603     }
604     case FT_DIRNOCHG:
605     case FT_NOCHG:
606 +      /* TODO: in accurate mode, we have to change NOCHG attribute to FT_REG... */
607 +//      if (!accurate_check_file(jcr, ff_pkt, false)) {
608 +//      Jmsg(jcr, M_SKIPPED, 1, _("     Unchanged file skipped: %s\n"), ff_pkt->fname);
609 +//      return 1;
610 +//      }
611 +      accurate_check_file(jcr, ff_pkt, false);
612        Jmsg(jcr, M_SKIPPED, 1, _("     Unchanged file skipped: %s\n"), ff_pkt->fname);
613        return 1;
614     case FT_ISARCH:
615 +      /* TODO: in accurate mode, we have to change NOCHG attribute to FT_REG... */
616 +//      if (!accurate_check_file(jcr, ff_pkt, false)) { 
617 +//      Jmsg(jcr, M_NOTSAVED, 0, _("     Archive file not saved: %s\n"), ff_pkt->fname);
618 +//      return 1;
619 +//      }
620 +      accurate_check_file(jcr, ff_pkt, false);
621        Jmsg(jcr, M_NOTSAVED, 0, _("     Archive file not saved: %s\n"), ff_pkt->fname);
622        return 1;
623     case FT_NOOPEN: {
624 @@ -1118,6 +1350,9 @@
625     }
626     unstrip_path(ff_pkt);
627  
628 +   /* list backuped files */
629 +   accurate_check_file(jcr, ff_pkt, true);
630 +
631     Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
632     if (!stat) {
633        Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
634 @@ -1128,6 +1363,58 @@
635     return true;
636  }
637  
638 +static bool encode_and_send_deleted_file(JCR *jcr, char *fname) 
639 +{
640 +   BSOCK *sd = jcr->store_bsock;
641 +   char *attribs;
642 +   char *attribsEx;
643 +   int stat;
644 +#ifdef FD_NO_SEND_TEST
645 +   return true;
646 +#endif
647 +
648 +   attribs = " ";
649 +   attribsEx = " ";
650 +
651 +   /*
652 +    * Send Attributes header to Storage daemon
653 +    *    <file-index> <stream> <info>
654 +    */
655 +   if (!sd->fsend("%ld %d 0", 0, STREAM_UNIX_ATTRIBUTES)) {
656 +      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
657 +            sd->bstrerror());
658 +      return false;
659 +   }
660 +   Dmsg1(300, ">stored: attrhdr %s\n", sd->msg);
661 +
662 +   /*
663 +    * Send file attributes to Storage daemon
664 +    *   File_index
665 +    *   File type
666 +    *   Filename (full path)
667 +    *   Encoded attributes
668 +    *   Link name (if type==FT_LNK or FT_LNKSAVED)
669 +    *   Encoded extended-attributes (for Win32)
670 +    *
671 +    * For a directory, link is the same as fname, but with trailing
672 +    * slash. For a linked file, link is the link.
673 +    */
674 +   stat = sd->fsend("%ld %d %s%c%s%c%s%c%s%c", 
675 +                   0 /* FileIndex */,
676 +                   FT_NOSTAT /* FileType */,
677 +                   fname /* FileName */, 
678 +                   0, attribs, 0, 0, 0, attribsEx, 0);
679 +
680 +   Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
681 +   if (!stat) {
682 +      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
683 +            sd->bstrerror());
684 +      return false;
685 +   }
686 +   sd->signal(BNET_EOD);            /* indicate end of attributes data */
687 +   return true;
688 +}
689 +
690  /* 
691   * Do in place strip of path
692   */
693 Index: src/filed/job.c
694 ===================================================================
695 --- src/filed/job.c     (révision 6443)
696 +++ src/filed/job.c     (copie de travail)
697 @@ -49,6 +49,7 @@
698  /* Imported functions */
699  extern int status_cmd(JCR *jcr);
700  extern int qstatus_cmd(JCR *jcr);
701 +extern int accurate_get_current_file_list_cmd(JCR *jcr);
702  
703  /* Forward referenced functions */
704  static int backup_cmd(JCR *jcr);
705 @@ -106,6 +107,7 @@
706     {"RunBeforeJob", runbefore_cmd, 0},
707     {"RunAfterJob",  runafter_cmd,  0},
708     {"Run",          runscript_cmd, 0},
709 +   {"accurate",     accurate_get_current_file_list_cmd, 0},
710     {NULL,       NULL}                  /* list terminator */
711  };
712  
713 @@ -1087,6 +1089,9 @@
714        case 'c':
715           fo->flags |= FO_CHKCHANGES;
716           break;
717 +      case 'C':
718 +         fo->flags |= FO_ACCURATE;
719 +         break;
720        default:
721           Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
722           break;
723 @@ -1195,6 +1200,9 @@
724  
725     level = get_memory(dir->msglen+1);
726     Dmsg1(110, "level_cmd: %s", dir->msg);
727 +   if (strstr(dir->msg, "accurate")) {
728 +      jcr->accurate = true;
729 +   }
730     if (sscanf(dir->msg, "level = %s ", level) != 1) {
731        goto bail_out;
732     }
733 @@ -1204,14 +1212,14 @@
734     /* Full backup requested? */
735     } else if (strcmp(level, "full") == 0) {
736        jcr->JobLevel = L_FULL;
737 -   } else if (strcmp(level, "differential") == 0) {
738 +   } else if (strstr(level, "differential")) {
739        jcr->JobLevel = L_DIFFERENTIAL;
740        free_memory(level);
741        return 1;
742 -   } else if (strcmp(level, "incremental") == 0) {
743 +   } else if (strstr(level, "incremental")) {
744        jcr->JobLevel = L_INCREMENTAL;
745        free_memory(level);
746 -      return 1;   
747 +      return 1;
748     /*
749      * We get his UTC since time, then sync the clocks and correct it
750      *   to agree with our clock.
751 Index: src/filed/restore.c
752 ===================================================================
753 --- src/filed/restore.c (révision 6443)
754 +++ src/filed/restore.c (copie de travail)
755 @@ -320,6 +320,11 @@
756              bclose(&rctx.bfd);
757           }
758  
759 +        /* TODO: manage deleted files */
760 +        if (file_index == 0) { /* deleted file */
761 +           continue;
762 +        }
763 +
764           /*
765            * Unpack attributes and do sanity check them
766            */
767 Index: src/cats/protos.h
768 ===================================================================
769 --- src/cats/protos.h   (révision 6443)
770 +++ src/cats/protos.h   (copie de travail)
771 @@ -102,8 +102,8 @@
772  int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cdbr);
773  int db_get_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr);
774  bool db_get_query_dbids(JCR *jcr, B_DB *mdb, POOL_MEM &query, dbid_list &ids);
775 +bool db_get_file_list(B_DB *mdb, char *jobids, DB_RESULT_HANDLER *result_handler, void *ctx);
776  
777 -
778  /* sql_list.c */
779  enum e_list_type {
780     HORZ_LIST,
781 Index: src/cats/sql_get.c
782 ===================================================================
783 --- src/cats/sql_get.c  (révision 6443)
784 +++ src/cats/sql_get.c  (copie de travail)
785 @@ -898,8 +898,6 @@
786     return ok;
787  }
788  
789 -
790 -
791  /* Get Media Record
792   *
793   * Returns: false: on failure
794 @@ -1018,5 +1016,31 @@
795     return ok;
796  }
797  
798 +bool db_get_file_list(B_DB *mdb, char *jobids, DB_RESULT_HANDLER *result_handler, void *ctx)
799 +{
800 +   if (*jobids == 0) {
801 +      db_lock(mdb);
802 +      Mmsg(mdb->errmsg, _("ERR=JobIds are empty\n"));
803 +      db_unlock(mdb);
804 +      return false;
805 +   }
806  
807 +   POOL_MEM buf (PM_MESSAGE);
808 +   
809 +   Mmsg(buf,
810 + "SELECT Path.Path, Filename.Name, File.FileIndex, File.JobId, File.LStat "
811 + "FROM ( "
812 +  "SELECT max(FileId) as FileId, PathId, FilenameId "
813 +    "FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (%s)) AS F "
814 +   "GROUP BY PathId, FilenameId "
815 +  ") AS Temp "
816 + "JOIN Filename ON (Filename.FilenameId = Temp.FilenameId) "
817 + "JOIN Path ON (Path.PathId = Temp.PathId) "
818 + "JOIN File ON (File.FileId = Temp.FileId) "
819 + "WHERE File.FileIndex > 0 ",
820 +            jobids);
821 +
822 +   return db_sql_query(mdb, buf.c_str(), result_handler, ctx);
823 +}
824 +
825  #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/
826 Index: src/stored/bextract.c
827 ===================================================================
828 --- src/stored/bextract.c       (révision 6443)
829 +++ src/stored/bextract.c       (copie de travail)
830 @@ -324,6 +324,14 @@
831           Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
832        }
833  
834 +      /* handle deleted file 
835 +       */
836 +      if (rec->FileIndex == 0) {
837 +        /* if file is included, remove it ? */
838 +        Jmsg(jcr, M_INFO, 0, _("fname=%s is marked as deleted.\n"), attr->fname);
839 +        break;
840 +      }
841 +
842        if (attr->file_index != rec->FileIndex) {
843           Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
844              rec->FileIndex, attr->file_index);
845 Index: src/stored/bscan.c
846 ===================================================================
847 --- src/stored/bscan.c  (révision 6443)
848 +++ src/stored/bscan.c  (copie de travail)
849 @@ -648,6 +648,15 @@
850     case STREAM_UNIX_ATTRIBUTES:
851     case STREAM_UNIX_ATTRIBUTES_EX:
852  
853 +      /* handle deleted file 
854 +       */
855 +      if (rec->FileIndex == 0) {
856 +        create_file_attributes_record(db, mjcr, attr->fname, attr->lname,
857 +                                      FT_NOSTAT, "", rec);
858 +        free_jcr(mjcr);
859 +        break;
860 +      }
861 +
862        if (!unpack_attributes_record(bjcr, rec->Stream, rec->data, attr)) {
863           Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
864        }
865 Index: src/stored/append.c
866 ===================================================================
867 --- src/stored/append.c (révision 6443)
868 +++ src/stored/append.c (copie de travail)
869 @@ -146,7 +146,7 @@
870  
871        /* Read Stream header from the File daemon.
872         *  The stream header consists of the following:
873 -       *    file_index (sequential Bacula file index, base 1)
874 +       *    file_index (sequential Bacula file index, base 1, 0 for deleted files)
875         *    stream     (Bacula number to distinguish parts of data)
876         *    info       (Info for Storage daemon -- compressed, encryped, ...)
877         *       info is not currently used, so is read, but ignored!
878 @@ -185,16 +185,18 @@
879  
880        Dmsg2(890, "<filed: Header FilInx=%d stream=%d\n", file_index, stream);
881  
882 -      if (!(file_index > 0 && (file_index == last_file_index ||
883 -          file_index == last_file_index + 1))) {
884 -         Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
885 -         ok = false;
886 -         break;
887 +      if (file_index != 0) {   /* TODO: handle file_index == 0 */
888 +        if (!(file_index > 0 && (file_index == last_file_index ||
889 +                                 file_index == last_file_index + 1))) {
890 +           Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
891 +           ok = false;
892 +           break;
893 +        }
894 +        if (file_index != last_file_index) {
895 +           jcr->JobFiles = file_index;
896 +           last_file_index = file_index;
897 +        }
898        }
899 -      if (file_index != last_file_index) {
900 -         jcr->JobFiles = file_index;
901 -         last_file_index = file_index;
902 -      }
903  
904        /* Read data stream from the File daemon.
905         *  The data stream is just raw bytes
906 @@ -212,25 +214,26 @@
907              stream_to_ascii(buf1, rec.Stream,rec.FileIndex),
908              rec.data_len);
909  
910 -         while (!write_record_to_block(dcr->block, &rec)) {
911 -            Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
912 -                       rec.remainder);
913 -            if (!write_block_to_device(dcr)) {
914 -               Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
915 -                  dev->print_name(), dev->bstrerror());
916 -               ok = false;
917 -               break;
918 -            }
919 -         }
920 -         if (!ok) {
921 -            Dmsg0(400, "Not OK\n");
922 -            break;
923 -         }
924 -         jcr->JobBytes += rec.data_len;   /* increment bytes this job */
925 -         Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
926 -            FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
927 -            stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
928 +        while (!write_record_to_block(dcr->block, &rec)) {
929 +           Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
930 +                 rec.remainder);
931 +           if (!write_block_to_device(dcr)) {
932 +              Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
933 +                    dev->print_name(), dev->bstrerror());
934 +              ok = false;
935 +              break;
936 +           }
937  
938 +           if (!ok) {
939 +              Dmsg0(400, "Not OK\n");
940 +              break;
941 +           }
942 +           jcr->JobBytes += rec.data_len;   /* increment bytes this job */
943 +           Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
944 +                 FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
945 +                 stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
946 +        }
947 +
948           /* Send attributes and digest to Director for Catalog */
949           if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX ||
950               crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
951 Index: src/jcr.h
952 ===================================================================
953 --- src/jcr.h   (révision 6443)
954 +++ src/jcr.h   (copie de travail)
955 @@ -119,6 +119,7 @@
956  
957  /* Forward referenced structures */
958  class JCR;
959 +class htable;
960  struct FF_PKT;
961  struct B_DB;
962  struct ATTR_DBR;
963 @@ -318,6 +319,7 @@
964     CRYPTO_CTX crypto;                 /* Crypto ctx */
965     DIRRES* director;                  /* Director resource */
966     bool VSS;                          /* VSS used by FD */
967 +   htable *file_list;                 /* Previous file list (accurate mode) */
968  #endif /* FILE_DAEMON */
969  
970  
971 Index: src/lib/Makefile.in
972 ===================================================================
973 --- src/lib/Makefile.in (révision 6443)
974 +++ src/lib/Makefile.in (copie de travail)
975 @@ -29,7 +29,7 @@
976           res.c rwlock.c scan.c serial.c sha1.c \
977           signal.c smartall.c rblist.c tls.c tree.c \
978           util.c var.c watchdog.c workq.c btimers.c \
979 -         address_conf.c pythonlib.c breg.c
980 +         address_conf.c pythonlib.c breg.c htable.c
981  
982  
983  LIBOBJS = attr.o base64.o berrno.o bsys.o bget_msg.o \
984 @@ -42,7 +42,7 @@
985           res.o rwlock.o scan.o serial.o sha1.o \
986           signal.o smartall.o rblist.o tls.o tree.o \
987           util.o var.o watchdog.o workq.o btimers.o \
988 -         address_conf.o pythonlib.o breg.o
989 +         address_conf.o pythonlib.o breg.o htable.o
990  
991  
992  EXTRAOBJS = @OBJLIST@
993 Index: src/findlib/find.h
994 ===================================================================
995 --- src/findlib/find.h  (révision 6443)
996 +++ src/findlib/find.h  (copie de travail)
997 @@ -108,6 +108,7 @@
998  #define FO_ENHANCEDWILD (1<<23)       /* Enhanced wild card processing */
999  #define FO_CHKCHANGES   (1<<24)       /* Check if file have been modified during backup */
1000  #define FO_STRIPPATH    (1<<25)       /* Check for stripping path */
1001 +#define FO_ACCURATE     (1<<26)       /* Accurate mode */
1002  
1003  struct s_included_file {
1004     struct s_included_file *next;