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