]> 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 @@ -44,6 +44,7 @@
58  #include "bacula.h"
59  #include "dird.h"
60  #include "ua.h"
61 +#include "findlib/find.h"
62  
63  /* Commands sent to File daemon */
64  static char backupcmd[] = "backup\n";
65 @@ -96,7 +97,85 @@
66     return true;
67  }
68  
69 +static int accurate_list_handler(void *ctx, int num_fields, char **row)
70 +{
71 +   JCR *jcr = (JCR *)ctx;
72 +
73 +   if (job_canceled(jcr)) {
74 +      return 1;
75 +   }
76 +   
77 +   if (row[0] > 0) {
78 +      jcr->file_bsock->fsend("%s%s%c%s", row[1], row[2], 0, row[3]); 
79 +   }
80 +   return 0;
81 +}
82 +
83 +bool db_accurate_get_jobids(JCR *jcr, POOLMEM *jobids)
84 +{
85 +   pm_strcpy(jobids, "13");
86 +   return 1;
87 +}
88 +
89 +bool send_accurate_current_files(JCR *jcr)
90 +{
91 +   char buf[MAXSTRING];
92 +   char ed1[50], ed2[50];
93 +
94 +   if (jcr->accurate == false || job_canceled(jcr) || jcr->JobLevel == L_FULL) {
95 +      return true;
96 +   }
97 +
98 +   POOLMEM *jobids = get_pool_memory(PM_FNAME);
99 +   db_accurate_get_jobids(jcr, jobids);
100 +
101 +   bsnprintf(buf, sizeof(buf),
102 +             "CREATE TEMPORARY TABLE btemp2%s AS ( "
103 +             "SELECT max(FileId) as FileId, PathId, FilenameId "
104 +             "FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (%s)) AS F "
105 +             "GROUP BY PathId, FilenameId ) ",
106 +             edit_uint64(jcr->JobId, ed1),
107 +             jobids);
108 +   db_sql_query(jcr->db, buf, NULL, NULL);
109 +
110 +   // TODO: compter le nombre de rows 
111 +   jcr->file_bsock->fsend("accurate files=%s\n", edit_uint64(5895, ed2)); /* TODO: change protocol to something like nb= */
112 +
113 +   bsnprintf(buf, sizeof(buf),
114 +            "SELECT File.FileIndex, Path.Path, Filename.Name, File.LStat "
115 +              "FROM btemp2%s JOIN Path USING (PathId) JOIN Filename USING (FilenameId) "
116 +              "JOIN File USING (FileId) "
117 +             "WHERE File.FileIndex > 0",
118 +            ed1, jobids);
119 +   db_sql_query(jcr->db, buf, accurate_list_handler, (void *)jcr);
120 +
121 +   bsnprintf(buf, sizeof(buf), "DROP TABLE btemp2%s", ed1);
122 +   free_pool_memory(jobids);
123 +
124  /*
125 + CREATE TEMPORARY TABLE btemp2 AS (
126 +  SELECT max(FileId) as FileId, PathId, FilenameId 
127 +    FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (39867,40341)) AS F
128 +   GROUP BY PathId, FilenameId )
129 +
130 +  SELECT File.FileIndex, Path.Path, Filename.Name, File.LStat
131 +    FROM btemp2 JOIN Path USING (PathId) JOIN Filename USING (FilenameId)
132 +                JOIN File USING (FileId)
133 +   WHERE File.FileIndex > 0
134 +
135 + DROP TABLE btemp2
136 +*/
137 +/*
138 +SELECT DISTINCT ON (PathId, FilenameId) FileIndex, Path, Name, LStat
139 +  FROM File JOIN Filename USING (FilenameId) JOIN Path USING (PathId) WHERE JobId IN (40341)
140 + ORDER BY PathId, FilenameId, JobId DESC
141 +*/
142 +
143 +   jcr->file_bsock->signal(BNET_EOD);
144 +   return true;
145 +}
146 +
147 +/*
148   * Do a backup of the specified FileSet
149   *
150   *  Returns:  false on failure
151 @@ -225,6 +304,14 @@
152        Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
153     }
154  
155 +   /*
156 +    * If backup is in accurate mode, FD will send the list of
157 +    * all files.
158 +    */
159 +   if (!send_accurate_current_files(jcr)) {
160 +      goto bail_out;
161 +   }
162 +
163     /* Send backup command */
164     fd->fsend(backupcmd);
165     if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
166 @@ -234,6 +321,7 @@
167     /* Pickup Job termination data */
168     stat = wait_for_job_termination(jcr);
169     db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
170 +
171     if (stat == JS_Terminated) {
172        backup_cleanup(jcr, stat);
173        return true;
174 Index: src/dird/inc_conf.c
175 ===================================================================
176 --- src/dird/inc_conf.c (révision 6443)
177 +++ src/dird/inc_conf.c (copie de travail)
178 @@ -94,6 +94,7 @@
179   * Items that are valid in an Options resource
180   */
181  static RES_ITEM options_items[] = {
182 +   {"accurate",        store_opts,    {0},     0, 0, 0},
183     {"compression",     store_opts,    {0},     0, 0, 0},
184     {"signature",       store_opts,    {0},     0, 0, 0},
185     {"verify",          store_opts,    {0},     0, 0, 0},
186 @@ -153,7 +154,8 @@
187     INC_KW_NOATIME,
188     INC_KW_ENHANCEDWILD,
189     INC_KW_CHKCHANGES,
190 -   INC_KW_STRIPPATH
191 +   INC_KW_STRIPPATH,
192 +   INC_KW_ACCURATE
193  };
194  
195  /*
196 @@ -163,6 +165,7 @@
197   *   options given above.
198   */
199  static struct s_kw FS_option_kw[] = {
200 +   {"accurate",    INC_KW_ACCURATE},
201     {"compression", INC_KW_COMPRESSION},
202     {"signature",   INC_KW_DIGEST},
203     {"encryption",  INC_KW_ENCRYPTION},
204 @@ -251,6 +254,8 @@
205     {"no",       INC_KW_ENHANCEDWILD,  "0"},
206     {"yes",      INC_KW_CHKCHANGES,    "c"},
207     {"no",       INC_KW_CHKCHANGES,    "0"},
208 +   {"yes",      INC_KW_ACCURATE,      "C"},
209 +   {"no",       INC_KW_ACCURATE,      "0"},
210     {NULL,       0,                      0}
211  };
212  
213 Index: src/filed/backup.c
214 ===================================================================
215 --- src/filed/backup.c  (révision 6443)
216 +++ src/filed/backup.c  (copie de travail)
217 @@ -48,8 +48,213 @@
218  static bool crypto_session_start(JCR *jcr);
219  static void crypto_session_end(JCR *jcr);
220  static bool crypto_session_send(JCR *jcr, BSOCK *sd);
221 +static bool encode_and_send_deleted_file(JCR *jcr, char *fname);
222  
223 +#include "lib/htable.c"
224 +typedef struct CurFile {
225 +   char *fname;
226 +   char *lstat;
227 +   hlink link;
228 +} CurFile;
229 +
230  /*
231 + * This function is called for each file seen in fileset.
232 + * 
233 + * If the file is skipped (attr=NULL), we will check if this
234 + * file have been backuped before. If not, we decide to backup it.
235 + *
236 + * If the file have attr=AAA, we check attr with lstat
237 + * 
238 + */
239 +/* TODO: tweak verify code to use the same function */
240 +bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt, bool saved)
241 +{
242 +   char *p;
243 +   int stat=false;
244 +   struct stat statc;                 /* catalog stat */
245 +   char *Opts_Digest = ff_pkt->VerifyOpts;
246 +   char *fname = ff_pkt->fname;
247 +   CurFile *elt;
248 +
249 +   int32_t LinkFIc;
250 +
251 +   if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
252 +      return true;
253 +   }
254 +
255 +   if (ff_pkt->type == FT_DIREND || ff_pkt->type == FT_REPARSE || ff_pkt->type == FT_DIRNOCHG) {
256 +      fname = ff_pkt->link;
257 +   } 
258 +
259 +   // TODO: check for /path/ and /path/file
260 +   elt = (CurFile *) jcr->file_list->lookup(fname);
261 +
262 +   if (!elt) {
263 +      // TODO: we must backup it !
264 +      Dmsg1(1, "accurate %s = yes (not found)\n", fname);
265 +      return true;
266 +   }
267 +
268 +   if (saved || *elt->lstat == '\0') {
269 +      Dmsg1(1, "accurate %s = no (already seen)\n", fname);
270 +      *elt->lstat = '\0';
271 +      return false;
272 +   }
273 +
274 +   decode_stat(elt->lstat, &statc, &LinkFIc); /* decode catalog stat */
275 +//   *do_Digest = CRYPTO_DIGEST_NONE;
276 +
277 +   for (p=Opts_Digest; *p; p++) {
278 +      char ed1[30], ed2[30];
279 +      switch (*p) {
280 +      case 'i':                /* compare INODEs */
281 +        if (statc.st_ino != ff_pkt->statp.st_ino) {
282 +           Jmsg(jcr, M_INFO, 0, _("      st_ino   differ. Cat: %s File: %s\n"),
283 +                edit_uint64((uint64_t)statc.st_ino, ed1),
284 +                edit_uint64((uint64_t)ff_pkt->statp.st_ino, ed2));
285 +           stat = true;
286 +        }
287 +        break;
288 +      case 'p':                /* permissions bits */
289 +        if (statc.st_mode != ff_pkt->statp.st_mode) {
290 +           Jmsg(jcr, M_INFO, 0, _("      st_mode  differ. Cat: %x File: %x\n"),
291 +                (uint32_t)statc.st_mode, (uint32_t)ff_pkt->statp.st_mode);
292 +           stat = true;
293 +        }
294 +        break;
295 +      case 'n':                /* number of links */
296 +        if (statc.st_nlink != ff_pkt->statp.st_nlink) {
297 +           Jmsg(jcr, M_INFO, 0, _("      st_nlink differ. Cat: %d File: %d\n"),
298 +                (uint32_t)statc.st_nlink, (uint32_t)ff_pkt->statp.st_nlink);
299 +           stat = true;
300 +        }
301 +        break;
302 +      case 'u':                /* user id */
303 +        if (statc.st_uid != ff_pkt->statp.st_uid) {
304 +           Jmsg(jcr, M_INFO, 0, _("      st_uid   differ. Cat: %u File: %u\n"),
305 +                (uint32_t)statc.st_uid, (uint32_t)ff_pkt->statp.st_uid);
306 +           stat = true;
307 +        }
308 +        break;
309 +      case 'g':                /* group id */
310 +        if (statc.st_gid != ff_pkt->statp.st_gid) {
311 +           Jmsg(jcr, M_INFO, 0, _("      st_gid   differ. Cat: %u File: %u\n"),
312 +                (uint32_t)statc.st_gid, (uint32_t)ff_pkt->statp.st_gid);
313 +           stat = true;
314 +        }
315 +        break;
316 +      case 's':                /* size */
317 +        if (statc.st_size != ff_pkt->statp.st_size) {
318 +           Jmsg(jcr, M_INFO, 0, _("      st_size  differ. Cat: %s File: %s\n"),
319 +                edit_uint64((uint64_t)statc.st_size, ed1),
320 +                edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
321 +           stat = true;
322 +        }
323 +        break;
324 +      case 'a':                /* access time */
325 +        if (statc.st_atime != ff_pkt->statp.st_atime) {
326 +           Jmsg(jcr, M_INFO, 0, _("      st_atime differs\n"));
327 +           stat = true;
328 +        }
329 +        break;
330 +      case 'm':
331 +        if (statc.st_mtime != ff_pkt->statp.st_mtime) {
332 +           Jmsg(jcr, M_INFO, 0, _("      st_mtime differs\n"));
333 +           stat = true;
334 +        }
335 +        break;
336 +      case 'c':                /* ctime */
337 +        if (statc.st_ctime != ff_pkt->statp.st_ctime) {
338 +           Jmsg(jcr, M_INFO, 0, _("      st_ctime differs\n"));
339 +           stat = true;
340 +        }
341 +        break;
342 +      case 'd':                /* file size decrease */
343 +        if (statc.st_size > ff_pkt->statp.st_size) {
344 +           Jmsg(jcr, M_INFO, 0, _("      st_size  decrease. Cat: %s File: %s\n"),
345 +                edit_uint64((uint64_t)statc.st_size, ed1),
346 +                edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
347 +           stat = true;
348 +        }
349 +        break;
350 +      case '5':                /* compare MD5 */
351 +        Dmsg1(500, "set Do_MD5 for %s\n", ff_pkt->fname);
352 +//      *do_Digest = CRYPTO_DIGEST_MD5;
353 +        break;
354 +      case '1':                 /* compare SHA1 */
355 +//      *do_Digest = CRYPTO_DIGEST_SHA1;
356 +        break;
357 +      case ':':
358 +      case 'V':
359 +      default:
360 +        break;
361 +      }
362 +   }
363 +   *elt->lstat = '\0';         /* mark it as deleted */
364 +   Dmsg2(1, "accurate %s = %i\n", fname, stat);
365 +   return stat;
366 +}
367 +
368 +int accurate_get_current_file_list_cmd(JCR *jcr)
369 +{
370 +   BSOCK *dir = jcr->dir_bsock;
371 +   int len;
372 +   uint64_t nb;
373 +   CurFile *elt=NULL;
374 +
375 +   if (jcr->accurate == false || job_canceled(jcr) || jcr->JobLevel == L_FULL) {
376 +      return true;
377 +   }
378 +
379 +   if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
380 +      dir->fsend(_("2991 Bad accurate command\n"));
381 +      return false;
382 +   }
383 +   
384 +   jcr->file_list = (htable *)malloc(sizeof(htable));
385 +   jcr->file_list->init(elt, &elt->link, nb);
386 +
387 +   /* get current files */
388 +   while (dir->recv() >= 0) {
389 +      len = strlen(dir->msg);
390 +      if ((len+1) < dir->msglen) {
391 +        elt = (CurFile *)malloc(sizeof(CurFile));
392 +        elt->fname  = (char *) malloc(dir->msglen+1);
393 +        memcpy(elt->fname, dir->msg, dir->msglen);
394 +        elt->fname[dir->msglen]='\0';
395 +        elt->lstat = elt->fname + len + 1;
396 +        Dmsg2(100, "hash[%s]=%s\n", elt->fname, elt->lstat);
397 +        jcr->file_list->insert(elt->fname, elt); 
398 +      }
399 +   }
400 +  
401 +   jcr->file_list->stats();
402 +//   dir->fsend("2000 OK accurate\n");
403 +
404 +   return true;
405 +}
406 +
407 +bool accurate_send_deleted_list(JCR *jcr)
408 +{
409 +   if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
410 +      return true;
411 +   }
412 +
413 +   CurFile *elt;
414 +   foreach_htable (elt, jcr->file_list) {
415 +      Dmsg3(100, "elt = 0x%x fname=%s lstat=%s\n", elt, elt->fname, elt->lstat);
416 +      if (*elt->lstat != '\0') {
417 +        encode_and_send_deleted_file(jcr, elt->fname);
418 +      }
419 +      free(elt->fname);
420 +   }
421 +   jcr->file_list->destroy();  /* TODO: clean htable when this function is not reached ? */
422 +   free(jcr->file_list);
423 +   jcr->file_list = NULL;
424 +   return true;
425 +}
426 +
427 +/*
428   * Find all the requested files and send them
429   * to the Storage daemon.
430   *
431 @@ -66,7 +271,6 @@
432     BSOCK *sd;
433     bool ok = true;
434     // TODO landonf: Allow user to specify encryption algorithm
435 -
436     sd = jcr->store_bsock;
437  
438     set_jcr_job_status(jcr, JS_Running);
439 @@ -134,7 +338,10 @@
440        ok = false;                     /* error */
441        set_jcr_job_status(jcr, JS_ErrorTerminated);
442     }
443 +   Dmsg1(1, "jcr->accurate == %i\n", jcr->accurate);
444  
445 +   accurate_send_deleted_list(jcr);              /* send deleted list to SD  */
446 +
447     free_pool_memory(jcr->acl_text);
448  
449     stop_heartbeat_monitor(jcr);
450 @@ -354,9 +561,19 @@
451     }
452     case FT_DIRNOCHG:
453     case FT_NOCHG:
454 +      /* TODO: in accurate mode, we have to change NOCHG attribute to FT_REG... */
455 +//      if (!accurate_check_file(jcr, ff_pkt, false)) {
456 +//      Jmsg(jcr, M_SKIPPED, 1, _("     Unchanged file skipped: %s\n"), ff_pkt->fname);
457 +//      return 1;
458 +//      }
459        Jmsg(jcr, M_SKIPPED, 1, _("     Unchanged file skipped: %s\n"), ff_pkt->fname);
460        return 1;
461     case FT_ISARCH:
462 +      /* TODO: in accurate mode, we have to change NOCHG attribute to FT_REG... */
463 +//      if (!accurate_check_file(jcr, ff_pkt, false)) { 
464 +//      Jmsg(jcr, M_NOTSAVED, 0, _("     Archive file not saved: %s\n"), ff_pkt->fname);
465 +//      return 1;
466 +//      }
467        Jmsg(jcr, M_NOTSAVED, 0, _("     Archive file not saved: %s\n"), ff_pkt->fname);
468        return 1;
469     case FT_NOOPEN: {
470 @@ -1118,6 +1335,9 @@
471     }
472     unstrip_path(ff_pkt);
473  
474 +   /* list backuped files */
475 +   accurate_check_file(jcr, ff_pkt, true);
476 +
477     Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
478     if (!stat) {
479        Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
480 @@ -1128,6 +1348,58 @@
481     return true;
482  }
483  
484 +static bool encode_and_send_deleted_file(JCR *jcr, char *fname) 
485 +{
486 +   BSOCK *sd = jcr->store_bsock;
487 +   char *attribs;
488 +   char *attribsEx;
489 +   int stat;
490 +#ifdef FD_NO_SEND_TEST
491 +   return true;
492 +#endif
493 +
494 +   attribs = " ";
495 +   attribsEx = " ";
496 +
497 +   /*
498 +    * Send Attributes header to Storage daemon
499 +    *    <file-index> <stream> <info>
500 +    */
501 +   if (!sd->fsend("%ld %d 0", 0, STREAM_UNIX_ATTRIBUTES)) {
502 +      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
503 +            sd->bstrerror());
504 +      return false;
505 +   }
506 +   Dmsg1(300, ">stored: attrhdr %s\n", sd->msg);
507 +
508 +   /*
509 +    * Send file attributes to Storage daemon
510 +    *   File_index
511 +    *   File type
512 +    *   Filename (full path)
513 +    *   Encoded attributes
514 +    *   Link name (if type==FT_LNK or FT_LNKSAVED)
515 +    *   Encoded extended-attributes (for Win32)
516 +    *
517 +    * For a directory, link is the same as fname, but with trailing
518 +    * slash. For a linked file, link is the link.
519 +    */
520 +   stat = sd->fsend("%ld %d %s%c%s%c%s%c%s%c", 
521 +                   0 /* FileIndex */,
522 +                   FT_NOSTAT /* FileType */,
523 +                   fname /* FileName */, 
524 +                   0, attribs, 0, 0, 0, attribsEx, 0);
525 +
526 +   Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
527 +   if (!stat) {
528 +      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
529 +            sd->bstrerror());
530 +      return false;
531 +   }
532 +   sd->signal(BNET_EOD);            /* indicate end of attributes data */
533 +   return true;
534 +}
535 +
536  /* 
537   * Do in place strip of path
538   */
539 Index: src/filed/job.c
540 ===================================================================
541 --- src/filed/job.c     (révision 6443)
542 +++ src/filed/job.c     (copie de travail)
543 @@ -49,6 +49,7 @@
544  /* Imported functions */
545  extern int status_cmd(JCR *jcr);
546  extern int qstatus_cmd(JCR *jcr);
547 +extern int accurate_get_current_file_list_cmd(JCR *jcr);
548  
549  /* Forward referenced functions */
550  static int backup_cmd(JCR *jcr);
551 @@ -106,6 +107,7 @@
552     {"RunBeforeJob", runbefore_cmd, 0},
553     {"RunAfterJob",  runafter_cmd,  0},
554     {"Run",          runscript_cmd, 0},
555 +   {"accurate",     accurate_get_current_file_list_cmd, 0},
556     {NULL,       NULL}                  /* list terminator */
557  };
558  
559 @@ -1087,6 +1089,9 @@
560        case 'c':
561           fo->flags |= FO_CHKCHANGES;
562           break;
563 +      case 'C':
564 +         fo->flags |= FO_ACCURATE;
565 +         break;
566        default:
567           Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
568           break;
569 @@ -1195,6 +1200,9 @@
570  
571     level = get_memory(dir->msglen+1);
572     Dmsg1(110, "level_cmd: %s", dir->msg);
573 +   if (strstr(dir->msg, "accurate")) {
574 +      jcr->accurate = true;
575 +   }
576     if (sscanf(dir->msg, "level = %s ", level) != 1) {
577        goto bail_out;
578     }
579 @@ -1204,14 +1212,14 @@
580     /* Full backup requested? */
581     } else if (strcmp(level, "full") == 0) {
582        jcr->JobLevel = L_FULL;
583 -   } else if (strcmp(level, "differential") == 0) {
584 +   } else if (strstr(level, "differential")) {
585        jcr->JobLevel = L_DIFFERENTIAL;
586        free_memory(level);
587        return 1;
588 -   } else if (strcmp(level, "incremental") == 0) {
589 +   } else if (strstr(level, "incremental")) {
590        jcr->JobLevel = L_INCREMENTAL;
591        free_memory(level);
592 -      return 1;   
593 +      return 1;
594     /*
595      * We get his UTC since time, then sync the clocks and correct it
596      *   to agree with our clock.
597 Index: src/stored/append.c
598 ===================================================================
599 --- src/stored/append.c (révision 6443)
600 +++ src/stored/append.c (copie de travail)
601 @@ -146,7 +146,7 @@
602  
603        /* Read Stream header from the File daemon.
604         *  The stream header consists of the following:
605 -       *    file_index (sequential Bacula file index, base 1)
606 +       *    file_index (sequential Bacula file index, base 1, 0 for deleted files)
607         *    stream     (Bacula number to distinguish parts of data)
608         *    info       (Info for Storage daemon -- compressed, encryped, ...)
609         *       info is not currently used, so is read, but ignored!
610 @@ -185,16 +185,18 @@
611  
612        Dmsg2(890, "<filed: Header FilInx=%d stream=%d\n", file_index, stream);
613  
614 -      if (!(file_index > 0 && (file_index == last_file_index ||
615 -          file_index == last_file_index + 1))) {
616 -         Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
617 -         ok = false;
618 -         break;
619 +      if (file_index != 0) {   /* TODO: handle file_index == 0 */
620 +        if (!(file_index > 0 && (file_index == last_file_index ||
621 +                                 file_index == last_file_index + 1))) {
622 +           Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
623 +           ok = false;
624 +           break;
625 +        }
626 +        if (file_index != last_file_index) {
627 +           jcr->JobFiles = file_index;
628 +           last_file_index = file_index;
629 +        }
630        }
631 -      if (file_index != last_file_index) {
632 -         jcr->JobFiles = file_index;
633 -         last_file_index = file_index;
634 -      }
635  
636        /* Read data stream from the File daemon.
637         *  The data stream is just raw bytes
638 @@ -212,25 +214,26 @@
639              stream_to_ascii(buf1, rec.Stream,rec.FileIndex),
640              rec.data_len);
641  
642 -         while (!write_record_to_block(dcr->block, &rec)) {
643 -            Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
644 -                       rec.remainder);
645 -            if (!write_block_to_device(dcr)) {
646 -               Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
647 -                  dev->print_name(), dev->bstrerror());
648 -               ok = false;
649 -               break;
650 -            }
651 -         }
652 -         if (!ok) {
653 -            Dmsg0(400, "Not OK\n");
654 -            break;
655 -         }
656 -         jcr->JobBytes += rec.data_len;   /* increment bytes this job */
657 -         Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
658 -            FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
659 -            stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
660 +        while (!write_record_to_block(dcr->block, &rec)) {
661 +           Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
662 +                 rec.remainder);
663 +           if (!write_block_to_device(dcr)) {
664 +              Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
665 +                    dev->print_name(), dev->bstrerror());
666 +              ok = false;
667 +              break;
668 +           }
669  
670 +           if (!ok) {
671 +              Dmsg0(400, "Not OK\n");
672 +              break;
673 +           }
674 +           jcr->JobBytes += rec.data_len;   /* increment bytes this job */
675 +           Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
676 +                 FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
677 +                 stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
678 +        }
679 +
680           /* Send attributes and digest to Director for Catalog */
681           if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX ||
682               crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
683 Index: src/jcr.h
684 ===================================================================
685 --- src/jcr.h   (révision 6443)
686 +++ src/jcr.h   (copie de travail)
687 @@ -119,6 +119,7 @@
688  
689  /* Forward referenced structures */
690  class JCR;
691 +class htable;
692  struct FF_PKT;
693  struct B_DB;
694  struct ATTR_DBR;
695 @@ -318,6 +319,7 @@
696     CRYPTO_CTX crypto;                 /* Crypto ctx */
697     DIRRES* director;                  /* Director resource */
698     bool VSS;                          /* VSS used by FD */
699 +   htable *file_list;                 /* Previous file list (accurate mode) */
700  #endif /* FILE_DAEMON */
701  
702  
703 Index: src/findlib/find.h
704 ===================================================================
705 --- src/findlib/find.h  (révision 6443)
706 +++ src/findlib/find.h  (copie de travail)
707 @@ -108,6 +108,7 @@
708  #define FO_ENHANCEDWILD (1<<23)       /* Enhanced wild card processing */
709  #define FO_CHKCHANGES   (1<<24)       /* Check if file have been modified during backup */
710  #define FO_STRIPPATH    (1<<25)       /* Check for stripping path */
711 +#define FO_ACCURATE     (1<<26)       /* Accurate mode */
712  
713  struct s_included_file {
714     struct s_included_file *next;