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