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