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