1 Index: src/dird/fd_cmds.c
2 ===================================================================
3 --- src/dird/fd_cmds.c (révision 6372)
4 +++ src/dird/fd_cmds.c (copie de travail)
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";
15 bsnprintf(since, since_len, _(" (upgraded from %s)"),
16 level_to_str(jcr->JobLevel));
17 jcr->JobLevel = jcr->jr.JobLevel = L_FULL;
18 + /* look if we found the last accurate backup */
19 + } else if (jcr->accurate && !db_accurate_find_backupid(jcr, jcr->db, &jcr->jr)) {
20 + Jmsg(jcr, M_INFO, 0, _("No prior or suitable Full accurate backup found in catalog. Doing FULL backup.\n"));
21 + bsnprintf(since, since_len, _(" (upgraded from %s)"),
22 + level_to_str(jcr->JobLevel));
23 + jcr->JobLevel = jcr->jr.JobLevel = L_FULL;
25 if (jcr->job->rerun_failed_levels) {
26 if (db_find_failed_job_since(jcr, jcr->db, &jcr->jr, jcr->stime, JobLevel)) {
30 stime = str_to_utime(jcr->stime);
31 - fd->fsend(levelcmd, NT_("since_utime "), edit_uint64(stime, ed1), 0);
32 + fd->fsend(levelcmd, "", NT_("since_utime "), edit_uint64(stime, ed1), 0);
33 while (bget_dirmsg(fd) >= 0) { /* allow him to poll us to sync clocks */
34 Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg);
37 bool send_level_command(JCR *jcr)
39 BSOCK *fd = jcr->file_bsock;
40 + const char *accurate=jcr->job->accurate?"accurate_":"";
42 * Send Level command to File daemon
44 switch (jcr->JobLevel) {
46 - fd->fsend(levelcmd, "base", " ", 0);
47 + fd->fsend(levelcmd, "", "base", " ", 0);
49 /* L_NONE is the console, sending something off to the FD */
52 - fd->fsend(levelcmd, "full", " ", 0);
53 + fd->fsend(levelcmd, "", "full", " ", 0);
56 - fd->fsend(levelcmd, "differential", " ", 0);
57 + fd->fsend(levelcmd, accurate, "differential", " ", 0);
61 - fd->fsend(levelcmd, "incremental", " ", 0);
62 + fd->fsend(levelcmd, accurate, "incremental", " ", 0);
66 Index: src/dird/backup.c
67 ===================================================================
68 --- src/dird/backup.c (révision 6372)
69 +++ src/dird/backup.c (copie de travail)
74 +#include "findlib/find.h"
76 /* Commands sent to File daemon */
77 static char backupcmd[] = "backup\n";
82 +static int accurate_list_handler(void *ctx, int num_fields, char **row)
84 + JCR *jcr = (JCR *)ctx;
86 + if (job_canceled(jcr)) {
91 + jcr->file_bsock->fsend("%s%s%c%s", row[1], row[2], 0, row[3]);
96 +bool db_get_jobids(JCR *jcr)
101 +bool accurate_send_current_files(JCR *jcr)
103 + char buf[MAXSTRING];
104 + char ed1[50], ed2[50];
106 + CREATE TEMPORARY TABLE btemp2 AS (
107 + SELECT max(FileId) as FileId, PathId, FilenameId
108 + FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (39867,40341)) AS F
109 + GROUP BY PathId, FilenameId )
111 + SELECT File.FileIndex, Path.Path, Filename.Name, File.LStat
112 + FROM btemp2 JOIN Path USING (PathId) JOIN Filename USING (FilenameId)
113 + JOIN File USING (FileId)
114 + WHERE File.FileIndex > 0
118 +SELECT DISTINCT ON (PathId, FilenameId) FileIndex, Path, Name, LStat
119 + FROM File JOIN Filename USING (FilenameId) JOIN Path USING (PathId) WHERE JobId IN (40341)
120 + ORDER BY PathId, FilenameId, JobId DESC
122 + bsnprintf(buf, sizeof(buf),
123 + "SELECT DISTINCT ON (PathId, FilenameId) FileIndex, Path, Name, LStat "
124 + "FROM File JOIN Filename USING (FilenameId) JOIN Path USING (PathId) WHERE JobId IN (%s)"
125 + "ORDER BY PathId, FilenameId, JobId DESC",
126 + "10,20,30"); /* jobid */
128 + Dmsg1(2, "display current files cmd=%s\n", buf);
129 + db_sql_query(jcr->db, buf, accurate_list_handler, (void *)jcr);
130 + jcr->file_bsock->signal(BNET_EOD);
135 + * We are called here for each record that matches the above
136 + * SQL query -- that is for each file contained in the Catalog
137 + * that was not marked earlier. This means that the file in
138 + * question is a missing file (in the Catalog but not on Disk).
140 +/* TODO: tweak verify code to use the same function */
141 +bool accurate_check_file(JCR *jcr, FILE_DBR *fdbr, char *attr, char *Opts_Digest, int *do_Digest)
145 + struct stat statf; /* file stat */
146 + struct stat statc; /* catalog stat */
148 + int32_t LinkFIf, LinkFIc;
150 + decode_stat(attr, &statf, &LinkFIf); /* decode file stat packet */
151 + decode_stat(fdbr->LStat, &statc, &LinkFIc); /* decode catalog stat */
152 + *do_Digest = CRYPTO_DIGEST_NONE;
154 + for (p=Opts_Digest; *p; p++) {
155 + char ed1[30], ed2[30];
157 + case 'i': /* compare INODEs */
158 + if (statc.st_ino != statf.st_ino) {
159 + Jmsg(jcr, M_INFO, 0, _(" st_ino differ. Cat: %s File: %s\n"),
160 + edit_uint64((uint64_t)statc.st_ino, ed1),
161 + edit_uint64((uint64_t)statf.st_ino, ed2));
165 + case 'p': /* permissions bits */
166 + if (statc.st_mode != statf.st_mode) {
167 + Jmsg(jcr, M_INFO, 0, _(" st_mode differ. Cat: %x File: %x\n"),
168 + (uint32_t)statc.st_mode, (uint32_t)statf.st_mode);
172 + case 'n': /* number of links */
173 + if (statc.st_nlink != statf.st_nlink) {
174 + Jmsg(jcr, M_INFO, 0, _(" st_nlink differ. Cat: %d File: %d\n"),
175 + (uint32_t)statc.st_nlink, (uint32_t)statf.st_nlink);
179 + case 'u': /* user id */
180 + if (statc.st_uid != statf.st_uid) {
181 + Jmsg(jcr, M_INFO, 0, _(" st_uid differ. Cat: %u File: %u\n"),
182 + (uint32_t)statc.st_uid, (uint32_t)statf.st_uid);
186 + case 'g': /* group id */
187 + if (statc.st_gid != statf.st_gid) {
188 + Jmsg(jcr, M_INFO, 0, _(" st_gid differ. Cat: %u File: %u\n"),
189 + (uint32_t)statc.st_gid, (uint32_t)statf.st_gid);
193 + case 's': /* size */
194 + if (statc.st_size != statf.st_size) {
195 + Jmsg(jcr, M_INFO, 0, _(" st_size differ. Cat: %s File: %s\n"),
196 + edit_uint64((uint64_t)statc.st_size, ed1),
197 + edit_uint64((uint64_t)statf.st_size, ed2));
201 + case 'a': /* access time */
202 + if (statc.st_atime != statf.st_atime) {
203 + Jmsg(jcr, M_INFO, 0, _(" st_atime differs\n"));
208 + if (statc.st_mtime != statf.st_mtime) {
209 + Jmsg(jcr, M_INFO, 0, _(" st_mtime differs\n"));
213 + case 'c': /* ctime */
214 + if (statc.st_ctime != statf.st_ctime) {
215 + Jmsg(jcr, M_INFO, 0, _(" st_ctime differs\n"));
219 + case 'd': /* file size decrease */
220 + if (statc.st_size > statf.st_size) {
221 + Jmsg(jcr, M_INFO, 0, _(" st_size decrease. Cat: %s File: %s\n"),
222 + edit_uint64((uint64_t)statc.st_size, ed1),
223 + edit_uint64((uint64_t)statf.st_size, ed2));
227 + case '5': /* compare MD5 */
228 + Dmsg1(500, "set Do_MD5 for %s\n", jcr->fname);
229 + *do_Digest = CRYPTO_DIGEST_MD5;
231 + case '1': /* compare SHA1 */
232 + *do_Digest = CRYPTO_DIGEST_SHA1;
244 + * This function is called at EOJ.
245 + * For a Full backup, we remove old one, and we add all entries
246 + * For an Incremental, we add all entries (delete have been before)
247 + * For a Differential, we add all entries (delete have been before)
252 +bool accurate_update_current_files(JCR *jcr)
256 + if (jcr->accurate == false) {
260 + backupid = db_accurate_find_backupid(jcr, jcr->db, &jcr->jr);
262 + Dmsg1(1, "backupid = %i\n", backupid);
265 + return false; /* something goes wrong */
268 + if (jcr->JobLevel == L_FULL) {
269 + db_accurate_cleanup_currentfile(jcr, jcr->db, backupid);
272 + db_accurate_update_currentfile(jcr, jcr->db, jcr->JobId,
273 + jcr->JobLevel, backupid);
278 + * We are called here for each record that matches the above
279 + * SQL query -- that is for each file contained in the Catalog
280 + * that was not marked earlier. This means that the file in
281 + * question is a missing file (in the Catalog but not on Disk).
283 +static int accurate_handler(void *ctx, int num_fields, char **row)
285 + JCR *jcr = (JCR *)ctx;
287 + if (job_canceled(jcr)) {
290 + if (num_fields == 2) { /* deleted files */
291 + jcr->file_bsock->fsend("%s%s", row[0]?row[0]:"", row[1]?row[1]:"");
292 + } else if (num_fields == 1) { /* files to backup */
293 + jcr->file_bsock->fsend("%s", row[0]?row[0]:"");
299 + * Send deleted files and files to backup in accurate mode
302 +static int accurate_send_missing_and_deleted_files(JCR *jcr, JobId_t BackupId)
304 + char buf[MAXSTRING];
305 + char ed1[50], ed2[50];
307 + bsnprintf(buf, sizeof(buf),
308 + "SELECT Name FROM ToBackup%s",
309 + edit_uint64(jcr->JobId, ed2));
311 + /* missing_handler is called for each file found */
312 + Dmsg1(2, "display files to backup cmd=%s\n", buf);
313 + db_sql_query(jcr->db, buf, accurate_handler, (void *)jcr);
314 + jcr->file_bsock->signal(BNET_EOD);
316 + bsnprintf(buf, sizeof(buf),
317 + "SELECT Path.Path,Filename.Name "
318 + "FROM CurrentFile "
319 + "JOIN File USING (FileId) "
320 + "JOIN Path USING (PathId) "
321 + "JOIN Filename USING (FilenameId) "
322 + "WHERE CurrentFile.BackupId=%s "
323 + "AND CurrentFile.MarkId!=%s ",
324 + edit_uint64(BackupId, ed1), edit_uint64(jcr->JobId, ed2));
325 + /* missing_handler is called for each file found */
326 + Dmsg1(2, "display deleted files cmd=%s\n", buf);
327 + db_sql_query(jcr->db, buf, accurate_handler, (void *)jcr);
328 + jcr->file_bsock->signal(BNET_EOD);
334 + * Accurate backup mode
335 + * 1. Receive the list of all files including those backed up to the Dir
336 + * 2. Dir computes files and deleted files.
337 + * 3. Dir sends list of additional files (new files) to backup,
338 + * and list of files deleted.
340 + * Cleanup attributes (don't use atime, inode etc..)
341 + * Need to insert file and attributes to temp table ?
342 + * Batch compare files and attributes ?
344 + * If file have file_index=0, they are discarded by FD
346 + * TODO: send deleted list and new list to client
347 + * tweak SD with file_index=-1
349 +bool accurate_compute_files(JCR *jcr)
352 + char buf[MAXSTRING];
355 + POOLMEM *fname = get_pool_memory(PM_MESSAGE);
356 + int do_Digest = CRYPTO_DIGEST_NONE;
357 + int32_t file_index = 0;
358 + JobId_t JobId=0; /* TODO: compute the job key in new table */
359 + JobId_t backupid=0;
361 + memset(&fdbr, 0, sizeof(FILE_DBR));
362 + fd = jcr->file_bsock;
363 + fdbr.JobId = JobId;
364 + jcr->FileIndex = 0;
366 + if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
370 + backupid = db_accurate_find_backupid(jcr, jcr->db, &jcr->jr);
372 + Jmsg(jcr, M_FATAL, 0, _("Can't use Accurate mode ERR=Can't find BackupId\n"));
375 + db_accurate_create_tobackup_table(jcr, jcr->db, jcr->JobId);
376 + Dmsg0(1, "bdird: waiting to receive file attributes\n");
378 + * Get Attributes and Signature from File daemon
382 + * Options or Digest (MD5/SHA1)
387 + while ((n=bget_dirmsg(fd)) >= 0 && !job_canceled(jcr)) {
389 + char *attr, *p, *fn;
390 + char Opts_Digest[MAXSTRING]; /* Verify Opts or MD5/SHA1 digest */
392 + if (job_canceled(jcr)) {
395 + fname = check_pool_memory_size(fname, fd->msglen);
396 + jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
397 + Dmsg1(1, "Atts+Digest=%s\n", fd->msg);
398 + if ((len = sscanf(fd->msg, "%ld %d %100s", &file_index, &stream,
400 + Jmsg3(jcr, M_FATAL, 0, _("bird<filed: bad attributes, expected 3 fields got %d\n"
401 +" mslen=%d msg=%s\n"), len, fd->msglen, fd->msg);
405 + * We read the Options or Signature into fname
406 + * to prevent overrun, now copy it to proper location.
408 + bstrncpy(Opts_Digest, fname, sizeof(Opts_Digest));
410 + skip_nonspaces(&p); /* skip FileIndex */
412 + skip_nonspaces(&p); /* skip Stream */
414 + skip_nonspaces(&p); /* skip Opts_Digest */
415 + p++; /* skip space */
418 + *fn++ = *p++; /* copy filename */
420 + *fn = *p++; /* term filename and point to attribs */
423 + * Got attributes stream, decode it
425 + if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
427 + Dmsg2(400, "file_index=%d attr=%s\n", file_index, attr);
429 + jcr->FileIndex = file_index; /* remember attribute file_index */
430 + do_Digest = CRYPTO_DIGEST_NONE;
431 + pm_strcpy(jcr->fname, fname); /* move filename into JCR */
435 + * Find equivalent record in the database
438 + if (db_accurate_get_file_attributes_record(jcr, jcr->db, jcr->fname,
441 + Dmsg2(1, "get_file ok fname=%s fileid=%i\n", jcr->fname, fdbr.FileId);
442 + if (fdbr.MarkId != jcr->JobId) { /* Already visited ? */
443 + if (file_index == 0) { /* file not saved */
444 + changed = accurate_check_file(jcr, &fdbr, attr, Opts_Digest, &do_Digest);
445 + Dmsg1(1, "check_file changed=%i\n", changed);
447 + if (changed == true) {
448 + db_accurate_mark_file_for_backup(jcr, jcr->db, jcr->fname, jcr->JobId);
449 + db_accurate_delete_file_record(jcr, jcr->db, fdbr.FileId, backupid);
451 + db_accurate_mark_file_record(jcr, jcr->db, backupid,
452 + fdbr.FileId, jcr->JobId);
454 + } else { /* file_index != 0 file have be backuped */
455 + db_accurate_delete_file_record(jcr, jcr->db, fdbr.FileId, backupid);
458 + Dmsg2(1, "already saved fname=%s fileid=%i\n", jcr->fname, fdbr.FileId);
460 + } else if (file_index == 0) {
461 + Dmsg1(1, "mark_for_backup fname=%s\n", jcr->fname);
462 + db_accurate_mark_file_for_backup(jcr, jcr->db, jcr->fname, jcr->JobId);
466 + * Got Digest Signature from Storage daemon
467 + * It came across in the Opts_Digest field.
470 + } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
471 + Dmsg2(400, "stream=Digest inx=%d Digest=%s\n", file_index, Opts_Digest);
473 + * When ever we get a digest it MUST have been
474 + * preceded by an attributes record, which sets attr_file_index
476 + if (jcr->FileIndex != (uint32_t)file_index) {
477 + Jmsg2(jcr, M_FATAL, 0, _("MD5/SHA1 index %d not same as attributes %d\n"),
478 + file_index, jcr->FileIndex);
481 + if (do_Digest != CRYPTO_DIGEST_NONE) {
482 + db_escape_string(jcr, jcr->db, buf, Opts_Digest, strlen(Opts_Digest));
483 + if (strcmp(buf, fdbr.Digest) != 0) {
484 + if (debug_level >= 10) {
485 + Jmsg(jcr, M_INFO, 0, _(" %d not same. File=%s Cat=%s\n"),
486 + stream, buf, fdbr.Digest);
488 + Jmsg(jcr, M_INFO, 0, _(" %d differs.\n"),
491 + //stat = JS_Differences;
493 + do_Digest = CRYPTO_DIGEST_NONE;
496 +// jcr->JobFiles = file_index;
498 + if (is_bnet_error(fd)) {
500 + Jmsg2(jcr, M_FATAL, 0, _("bdird<filed: bad attributes from filed n=%d : %s\n"),
501 + n, be.bstrerror());
506 +CREATE VIEW cf AS SELECT path.path || filename.name as filename,
507 + jobid, currentfile.markid, backupid
508 + FROM File join currentfile using (fileid) join filename using (filenameid) join path using (pathid)
511 + accurate_send_missing_and_deleted_files(jcr, backupid);
513 + db_accurate_clean_deleted_files(jcr, jcr->db, jcr->JobId, backupid);
515 + db_accurate_drop_tobackup_table(jcr, jcr->db, jcr->JobId);
517 + free_pool_memory(fname);
521 + db_accurate_drop_tobackup_table(jcr, jcr->db, jcr->JobId);
526 * Do a backup of the specified FileSet
528 * Returns: false on failure
534 + * If backup is in accurate mode, FD will send the list of
535 + * all files. We have to store it, and compute witch files
536 + * have been deleted and witch files have to be backuped.
538 + accurate_compute_files(jcr);
540 /* Pickup Job termination data */
541 stat = wait_for_job_termination(jcr);
542 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
543 + accurate_update_current_files(jcr);
545 if (stat == JS_Terminated) {
546 backup_cleanup(jcr, stat);
548 Index: src/dird/inc_conf.c
549 ===================================================================
550 --- src/dird/inc_conf.c (révision 6372)
551 +++ src/dird/inc_conf.c (copie de travail)
553 * Items that are valid in an Options resource
555 static RES_ITEM options_items[] = {
556 + {"accurate", store_opts, {0}, 0, 0, 0},
557 {"compression", store_opts, {0}, 0, 0, 0},
558 {"signature", store_opts, {0}, 0, 0, 0},
559 {"verify", store_opts, {0}, 0, 0, 0},
571 * options given above.
573 static struct s_kw FS_option_kw[] = {
574 + {"accurate", INC_KW_ACCURATE},
575 {"compression", INC_KW_COMPRESSION},
576 {"signature", INC_KW_DIGEST},
577 {"encryption", INC_KW_ENCRYPTION},
579 {"no", INC_KW_ENHANCEDWILD, "0"},
580 {"yes", INC_KW_CHKCHANGES, "c"},
581 {"no", INC_KW_CHKCHANGES, "0"},
582 + {"yes", INC_KW_ACCURATE, "C"},
583 + {"no", INC_KW_ACCURATE, "0"},
587 Index: src/filed/backup.c
588 ===================================================================
589 --- src/filed/backup.c (révision 6372)
590 +++ src/filed/backup.c (copie de travail)
592 static bool crypto_session_start(JCR *jcr);
593 static void crypto_session_end(JCR *jcr);
594 static bool crypto_session_send(JCR *jcr, BSOCK *sd);
595 +static bool encode_and_send_deleted_file(JCR *jcr, char *fname);
598 + * Called by save_file when accept/discard file for backup
600 + * TODO: we could add MD5/SHAX digest, but we have to compute it
603 +static bool accurate_add_file(JCR *jcr, FF_PKT *ff_pkt, char *stats)
606 + char attribs[MAXSTRING];
607 + uint32_t file_index=jcr->JobFiles;
608 + BSOCK *dir = jcr->dir_bsock;
611 + if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
615 + if (!stats) { /* TODO: don't always compute attribute */
617 + encode_stat(attribs, ff_pkt, 0);
621 + switch (ff_pkt->type) {
622 + case FT_LNKSAVED: /* Hard linked, file already saved */
624 + stat = dir->fsend("%d %d %s %s%c%s%c%s%c", file_index,
625 + STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname,
626 + 0, a, 0, ff_pkt->link, 0);
634 + stat = dir->fsend("%d %d %s %s%c%s%c%c", file_index,
635 + STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname,
643 + stat = dir->fsend("%d %d %s %s%c%s%c%c", file_index,
644 + STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->link,
648 + Dmsg2(1, _("Fname=%s Type=%i\n"), ff_pkt->fname, ff_pkt->type);
653 + Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), bnet_strerror(dir));
660 +/* build a fileset with new files from director */
661 +static bool accurate_get_new_and_deleted_file_list(JCR *jcr)
663 + BSOCK *dir = jcr->dir_bsock;
664 + if (jcr->accurate == false || job_canceled(jcr)) {
668 + /* get missing files */
669 + while (dir->recv() >= 0) {
670 + Dmsg1(1, "missing = %s\n", dir->msg);
673 + /* get deleted files */
674 + while (dir->recv() >= 0) {
675 + Dmsg1(1, "deleted = %s\n", dir->msg);
676 + encode_and_send_deleted_file(jcr, dir->msg);
682 +/* send deleted file list to stored */
683 +static bool accurate_send_deleted_list(JCR *jcr)
685 + if (jcr->accurate == false || job_canceled(jcr)) {
691 +static bool accurate_send_file_list(JCR *jcr)
693 + if (jcr->accurate == false || job_canceled(jcr)) {
696 + Dmsg0(1, "Sending BNET_EOD\n");
697 + jcr->dir_bsock->signal(BNET_EOD); /* end of sending data */
703 * Find all the requested files and send them
704 * to the Storage daemon.
709 // TODO landonf: Allow user to specify encryption algorithm
711 sd = jcr->store_bsock;
713 set_jcr_job_status(jcr, JS_Running);
715 ok = false; /* error */
716 set_jcr_job_status(jcr, JS_ErrorTerminated);
718 + Dmsg1(1, "jcr->accurate == %i\n", jcr->accurate);
719 + /* start accurate stuffs */
720 + if (jcr->accurate) {
721 + /* TODO: test job_canceled() */
722 + accurate_send_file_list(jcr); /* send all files to DIR */
723 + accurate_get_new_and_deleted_file_list(jcr); /* get a new incr fileset from DIR */
724 +// set_find_options((FF_PKT *)jcr->ff, 0, 0); /* we backup all that director wants */
725 +// if (!find_files(jcr, (FF_PKT *)jcr->ff, save_file)) {
726 +// ok = false; /* error */
727 +// set_jcr_job_status(jcr, JS_ErrorTerminated);
729 +// accurate_send_file_list(jcr); /* send all new files to DIR */
730 + accurate_send_deleted_list(jcr); /* send deleted list to SD */
733 free_pool_memory(jcr->acl_text);
738 Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname);
739 + accurate_add_file(jcr, ff_pkt, NULL); /* list skipped files */
742 Jmsg(jcr, M_NOTSAVED, 0, _(" Archive file not saved: %s\n"), ff_pkt->fname);
743 + accurate_add_file(jcr, ff_pkt, NULL); /* list skipped files */
747 @@ -1111,6 +1232,9 @@
749 unstrip_path(ff_pkt);
751 + /* list backuped files */
752 + accurate_add_file(jcr, ff_pkt, attribs);
754 Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
756 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
757 @@ -1121,6 +1245,58 @@
761 +static bool encode_and_send_deleted_file(JCR *jcr, char *fname)
763 + BSOCK *sd = jcr->store_bsock;
767 +#ifdef FD_NO_SEND_TEST
775 + * Send Attributes header to Storage daemon
776 + * <file-index> <stream> <info>
778 + if (!sd->fsend("%ld %d 0", 0, STREAM_UNIX_ATTRIBUTES)) {
779 + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
783 + Dmsg1(300, ">stored: attrhdr %s\n", sd->msg);
786 + * Send file attributes to Storage daemon
789 + * Filename (full path)
790 + * Encoded attributes
791 + * Link name (if type==FT_LNK or FT_LNKSAVED)
792 + * Encoded extended-attributes (for Win32)
794 + * For a directory, link is the same as fname, but with trailing
795 + * slash. For a linked file, link is the link.
797 + stat = sd->fsend("%ld %d %s%c%s%c%s%c%s%c",
799 + FT_NOSTAT /* FileType */,
800 + fname /* FileName */,
801 + 0, attribs, 0, 0, 0, attribsEx, 0);
803 + Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
805 + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
809 + sd->signal(BNET_EOD); /* indicate end of attributes data */
814 * Do in place strip of path
816 Index: src/filed/job.c
817 ===================================================================
818 --- src/filed/job.c (révision 6372)
819 +++ src/filed/job.c (copie de travail)
820 @@ -1087,6 +1087,9 @@
822 fo->flags |= FO_CHKCHANGES;
825 + fo->flags |= FO_ACCURATE;
828 Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
830 @@ -1195,6 +1198,9 @@
832 level = get_memory(dir->msglen+1);
833 Dmsg1(110, "level_cmd: %s", dir->msg);
834 + if (strstr(dir->msg, "accurate")) {
835 + jcr->accurate = true;
837 if (sscanf(dir->msg, "level = %s ", level) != 1) {
840 @@ -1204,14 +1210,14 @@
841 /* Full backup requested? */
842 } else if (strcmp(level, "full") == 0) {
843 jcr->JobLevel = L_FULL;
844 - } else if (strcmp(level, "differential") == 0) {
845 + } else if (strstr(level, "differential")) {
846 jcr->JobLevel = L_DIFFERENTIAL;
849 - } else if (strcmp(level, "incremental") == 0) {
850 + } else if (strstr(level, "incremental")) {
851 jcr->JobLevel = L_INCREMENTAL;
856 * We get his UTC since time, then sync the clocks and correct it
857 * to agree with our clock.
858 Index: src/cats/sql_update.c
859 ===================================================================
860 --- src/cats/sql_update.c (révision 6372)
861 +++ src/cats/sql_update.c (copie de travail)
866 +int db_accurate_delete_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t BackupId)
869 + char ed1[50], ed2[50];
871 + Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE FileId=%s AND BackupId=%s",
872 + edit_int64(FileId, ed1), edit_int64(BackupId, ed2));
873 + stat = INSERT_DB(jcr, mdb, mdb->cmd);
878 +int db_accurate_mark_file_for_backup(JCR *jcr, B_DB *mdb, char *fname, JobId_t JobId)
883 + /* TODO: mdb->esc_xxx are already ok but it's more smart to recompute it */
884 +// mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*len+2);
885 +// mdb->esc_name = db_escape_string(jcr, mdb, mdb->esc_name, fname, len);
886 + Mmsg(mdb->cmd, "INSERT INTO ToBackup%s (name) VALUES ('%s%s')", edit_int64(JobId, ed1), mdb->esc_path, mdb->esc_name);
887 + stat = INSERT_DB(jcr, mdb, mdb->cmd);
892 +int db_accurate_cleanup_currentfile(JCR *jcr, B_DB *mdb, JobId_t BackupId)
897 + Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE BackupId=%s", edit_int64(BackupId, ed1));
898 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
903 +int db_accurate_update_currentfile(JCR *jcr, B_DB *mdb, JobId_t JobId, int JobLevel, JobId_t BackupId)
906 + char ed1[50], ed2[50], ed3[50];
908 + edit_int64(JobId, ed2);
910 + "INSERT INTO CurrentFile (FileId, BackupId, FullMark, MarkId) "
911 + " (SELECT FileId, %s, '%c', %s FROM File WHERE JobId=%s)",
912 + edit_int64(BackupId, ed1),
913 + JobLevel, ed2, ed2);
914 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
919 +int db_accurate_create_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId)
924 + Mmsg(mdb->cmd, "CREATE TABLE ToBackup%s (name text)", edit_int64(JobId, ed1));
925 +// Mmsg(mdb->cmd, "CREATE TEMPORARY TABLE ToBackup%s (name text)", edit_int64(JobId, ed1));
926 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
931 +int db_accurate_drop_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId)
936 +// Mmsg(mdb->cmd, "DROP TABLE ToBackup%s", edit_int64(JobId, ed1));
937 +// stat = QUERY_DB(jcr, mdb, mdb->cmd);
943 +/* Mark the file record as being visited during database
944 + * accurate compare. Stuff JobId into the MarkId field
946 +int db_accurate_mark_file_record(JCR *jcr, B_DB *mdb, JobId_t BackupId, FileId_t FileId, JobId_t JobId)
949 + char ed1[50], ed2[50], ed3[50];
952 + Mmsg(mdb->cmd, "UPDATE CurrentFile SET MarkId=%s WHERE FileId=%s AND BackupId=%s",
953 + edit_int64(JobId, ed1), edit_int64(FileId, ed2), edit_int64(BackupId, ed3));
954 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
955 + if (!stat || sql_affected_rows(mdb) != 1) {
963 * Update the Job record at start of Job
965 Index: src/cats/drop_postgresql_tables.in
966 ===================================================================
967 --- src/cats/drop_postgresql_tables.in (révision 6372)
968 +++ src/cats/drop_postgresql_tables.in (copie de travail)
973 -$bindir/psql -f - -d ${db_name} $* <<END-OF-DATA
974 +$bindir/psql -f - -U regress -d ${db_name} $* <<END-OF-DATA
975 drop table unsavedfiles;
976 drop table basefiles;
978 Index: src/cats/make_postgresql_tables.in
979 ===================================================================
980 --- src/cats/make_postgresql_tables.in (révision 6372)
981 +++ src/cats/make_postgresql_tables.in (copie de travail)
986 -$bindir/psql -f - -d ${db_name} $* <<END-OF-DATA
987 +$bindir/psql -f - -U regress -d ${db_name} $* <<END-OF-DATA
989 CREATE TABLE filename
992 CREATE INDEX file_jobid_idx on file (jobid);
993 CREATE INDEX file_fp_idx on file (filenameid, pathid);
995 +CREATE TABLE CurrentBackupId
997 + BackupId serial not null,
998 + ClientId integer not null,
999 + JobName text not null,
1000 + FileSetId integer not null,
1001 + primary key (BackupId)
1004 +-- Serait bien de prendre la meme table pour
1005 +-- les File et le CurrentBackup...
1006 +-- Mais y'a des problemes pour les prunes
1008 +CREATE TABLE CurrentFile
1010 + FileId integer not null,
1011 + BackupId integer not null,
1012 + FullMark char(1) default 0,
1013 + MarkId integer default 0,
1014 + primary key (FileId)
1017 +CREATE INDEX currentfile_fileid on CurrentFile (BackupId);
1019 +-- CREATE TEMPORARY TABLE batch (fileindex int,
1026 +-- -- On batch insert dans la table temporaire
1028 +-- il faut trouver les fichiers manquant
1029 +-- INSERT des nouveaux, UPDATE des anciens, SELECT pour trouver les deletes
1032 +-- il faut trouver les fichiers modifies
1033 +-- Le champs LStat n'est plus le meme
1035 +-- FROM CurrentBackup,
1036 +-- batch JOIN Path USING (Path) JOIN Filename USING (Name)
1037 +-- WHERE Path.PathId = CurrentBackup.PathId
1038 +-- AND Filename.FilenameId = CurrentBackup.FilenameId
1039 +-- AND CurrentBackup.LStat != batch.LStat
1041 +-- il faut mettre a jour la liste des fichiers
1049 -- Possibly add one or more of the following indexes
1050 -- if your Verifies are too slow.
1051 Index: src/cats/protos.h
1052 ===================================================================
1053 --- src/cats/protos.h (révision 6372)
1054 +++ src/cats/protos.h (copie de travail)
1057 int db_delete_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pool_dbr);
1058 int db_delete_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr);
1059 +int db_accurate_clean_deleted_files(JCR *jcr, B_DB *mdb, JobId_t JobId, JobId_t BackupId);
1062 bool db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime);
1063 bool db_find_last_jobid(JCR *jcr, B_DB *mdb, const char *Name, JOB_DBR *jr);
1064 +JobId_t db_accurate_find_backupid(JCR *jcr, B_DB *mdb, JOB_DBR *jr);
1065 int db_find_next_volume(JCR *jcr, B_DB *mdb, int index, bool InChanger, MEDIA_DBR *mr);
1066 bool db_find_failed_job_since(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM *stime, int &JobLevel);
1069 +int db_accurate_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JobId_t backupid, FILE_DBR *fdbr);
1070 bool db_get_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pdbr);
1071 int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cr);
1072 bool db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr);
1073 @@ -129,6 +132,14 @@
1074 int db_update_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr);
1075 int db_add_digest_to_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, char *digest, int type);
1076 int db_mark_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t JobId);
1077 +int db_accurate_mark_file_for_backup(JCR *jcr, B_DB *mdb, char *fname, FileId_t JobId);
1078 +int db_accurate_mark_file_record(JCR *jcr, B_DB *mdb, JobId_t BackupId, FileId_t FileId, JobId_t JobId);
1079 +int db_accurate_drop_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId);
1080 +int db_accurate_create_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId);
1081 +int db_accurate_delete_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t BackupId);
1082 void db_make_inchanger_unique(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr);
1083 +int db_accurate_cleanup_currentfile(JCR *jcr, B_DB *mdb, JobId_t BackupId);
1084 +int db_accurate_update_currentfile(JCR *jcr, B_DB *mdb, JobId_t JobId, int JobLevel, JobId_t BackupId);
1087 #endif /* __SQL_PROTOS_H */
1088 Index: src/cats/sql_find.c
1089 ===================================================================
1090 --- src/cats/sql_find.c (révision 6372)
1091 +++ src/cats/sql_find.c (copie de travail)
1092 @@ -190,7 +190,64 @@
1097 + * Find BackupId of last job that ran. E.g. for
1099 + * Returns: Last backuip
1103 +db_accurate_find_backupid(JCR *jcr, B_DB *mdb, JOB_DBR *jr)
1106 + char ed1[50],ed2[50];
1107 + JobId_t backupid=0;
1109 + if (jcr->accurate == false) {
1113 + /* Find backupid */
1115 + Dmsg2(100, "JobLevel=%d JobType=%d\n", jcr->JobLevel, jcr->JobType);
1117 +"SELECT BackupId FROM CurrentBackupId WHERE JobName='%s' AND "
1118 +"ClientId=%s AND FileSetId=%s ORDER BY BackupId DESC LIMIT 1",
1120 + edit_int64(jr->ClientId, ed1),
1121 + edit_int64(jr->FileSetId, ed2));
1123 + Dmsg1(100, "Query: %s\n", mdb->cmd);
1124 + if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
1128 + if ((row = sql_fetch_row(mdb)) == NULL) {
1129 + sql_free_result(mdb);
1130 + if (jcr->JobLevel == L_FULL) {
1132 + "INSERT INTO CurrentBackupId (JobName, ClientId, FileSetId) VALUES ('%s', %s, %s)",
1133 + jr->Name, ed1, ed2);
1134 + if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
1138 + backupid = sql_insert_id(mdb, NT_("CurrentBackupId"));
1140 + Mmsg1(&mdb->errmsg, _("No Job found for: %s.\n"), mdb->cmd);
1144 + backupid = str_to_int64(row[0]);
1147 + sql_free_result(mdb);
1155 * Find JobId of last job that ran. E.g. for
1156 * VERIFY_CATALOG we want the JobId of the last INIT.
1157 Index: src/cats/sql_delete.c
1158 ===================================================================
1159 --- src/cats/sql_delete.c (révision 6372)
1160 +++ src/cats/sql_delete.c (copie de travail)
1161 @@ -236,5 +236,22 @@
1166 + * Purge delete file from CurrentFile table. This table contains only
1169 +int db_accurate_clean_deleted_files(JCR *jcr, B_DB *mdb, JobId_t JobId, JobId_t BackupId)
1172 + char ed1[50], ed2[50];
1174 + Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE MarkId!=%s AND BackupId=%s",
1175 + edit_int64(JobId, ed1), edit_int64(BackupId, ed2));
1176 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
1183 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/
1184 Index: src/cats/sql_create.c
1185 ===================================================================
1186 --- src/cats/sql_create.c (révision 6372)
1187 +++ src/cats/sql_create.c (copie de travail)
1188 @@ -829,6 +829,14 @@
1192 +bool db_accurate_insert(JCR *jcr, B_DB *mdb, bool saved, const char *fname, struct stat *stat)
1195 + split_path_and_file(jcr, mdb, fname);
1196 + /* make like in Verify code */
1201 * Create File record in B_DB
1203 Index: src/cats/sql_get.c
1204 ===================================================================
1205 --- src/cats/sql_get.c (révision 6372)
1206 +++ src/cats/sql_get.c (copie de travail)
1209 * Returns: 0 on failure
1210 * 1 on success with the File record in FILE_DBR
1212 + * TODO: optimize this with only one query
1214 int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JOB_DBR *jr, FILE_DBR *fdbr)
1221 + * Given a full filename (with path), look up the File record
1222 + * (with attributes) in the database.
1224 + * Returns: 0 on failure
1225 + * 1 on success with the File record in FILE_DBR
1227 +int db_accurate_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname,
1228 + JobId_t backupid, FILE_DBR *fdbr)
1235 + split_path_and_file(jcr, mdb, fname);
1237 + mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->fnl+2);
1238 + db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
1240 + mdb->esc_path = check_pool_memory_size(mdb->esc_path, 2*mdb->pnl+2);
1241 + db_escape_string(jcr, mdb, mdb->esc_path, mdb->path, mdb->pnl);
1243 + /* A file could be present more than one time in the same backup, so we use LIMIT 1 */
1245 +"SELECT FileId, LStat, MD5, FilenameId, PathId, FileIndex, CurrentFile.MarkId, JobId "
1246 + "FROM File JOIN CurrentFile USING (FileId) "
1247 + "JOIN Filename USING (FilenameId) "
1248 + "JOIN Path USING (PathId) "
1249 + "WHERE Path.Path='%s' "
1250 + "AND Filename.Name='%s' "
1251 + "AND BackupId=%s "
1252 + "ORDER BY FileId DESC LIMIT 1",
1255 + edit_int64(backupid, ed1));
1257 + Dmsg1(2,"get_file %s\n", mdb->cmd);
1259 + if (QUERY_DB(jcr, mdb, mdb->cmd)) {
1261 + mdb->num_rows = sql_num_rows(mdb);
1262 + if (mdb->num_rows == 1) {
1263 + if ((row = sql_fetch_row(mdb)) == NULL) {
1264 + Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
1266 + fdbr->FileId = str_to_int64(row[0]);
1267 + bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat));
1268 + bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest));
1269 + fdbr->FilenameId = str_to_int64(row[3]);
1270 + fdbr->PathId = str_to_int64(row[4]);
1271 + fdbr->FileIndex = str_to_int64(row[5]);
1272 + fdbr->MarkId = str_to_int64(row[6]);
1273 + fdbr->JobId = str_to_int64(row[7]);
1277 + sql_free_result(mdb);
1279 + Mmsg(mdb->errmsg, _("File record: %s not found in Catalog for BackupId=%s.\n"), fname, ed1);
1289 * Returns: 0 on failure
1290 Index: src/stored/append.c
1291 ===================================================================
1292 --- src/stored/append.c (révision 6372)
1293 +++ src/stored/append.c (copie de travail)
1294 @@ -185,16 +185,18 @@
1296 Dmsg2(890, "<filed: Header FilInx=%d stream=%d\n", file_index, stream);
1298 - if (!(file_index > 0 && (file_index == last_file_index ||
1299 - file_index == last_file_index + 1))) {
1300 - Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
1303 + if (file_index != 0) { /* TODO: handle file_index == 0 */
1304 + if (!(file_index > 0 && (file_index == last_file_index ||
1305 + file_index == last_file_index + 1))) {
1306 + Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
1310 + if (file_index != last_file_index) {
1311 + jcr->JobFiles = file_index;
1312 + last_file_index = file_index;
1315 - if (file_index != last_file_index) {
1316 - jcr->JobFiles = file_index;
1317 - last_file_index = file_index;
1320 /* Read data stream from the File daemon.
1321 * The data stream is just raw bytes
1322 @@ -212,24 +214,26 @@
1323 stream_to_ascii(buf1, rec.Stream,rec.FileIndex),
1326 - while (!write_record_to_block(dcr->block, &rec)) {
1327 - Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
1329 - if (!write_block_to_device(dcr)) {
1330 - Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
1331 - dev->print_name(), dev->bstrerror());
1337 - Dmsg0(400, "Not OK\n");
1340 - jcr->JobBytes += rec.data_len; /* increment bytes this job */
1341 - Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
1342 - FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
1343 - stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
1344 + if (file_index != 0) {
1345 + while (!write_record_to_block(dcr->block, &rec)) {
1346 + Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
1348 + if (!write_block_to_device(dcr)) {
1349 + Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
1350 + dev->print_name(), dev->bstrerror());
1356 + Dmsg0(400, "Not OK\n");
1359 + jcr->JobBytes += rec.data_len; /* increment bytes this job */
1360 + Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
1361 + FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
1362 + stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
1365 /* Send attributes and digest to Director for Catalog */
1366 if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX ||
1367 Index: src/findlib/find.h
1368 ===================================================================
1369 --- src/findlib/find.h (révision 6372)
1370 +++ src/findlib/find.h (copie de travail)
1372 #define FO_ENHANCEDWILD (1<<23) /* Enhanced wild card processing */
1373 #define FO_CHKCHANGES (1<<24) /* Check if file have been modified during backup */
1374 #define FO_STRIPPATH (1<<25) /* Check for stripping path */
1375 +#define FO_ACCURATE (1<<26) /* Accurate mode */
1377 struct s_included_file {
1378 struct s_included_file *next;
1379 Index: patches/testing/project-accurate-backup.patch
1380 ===================================================================
1381 --- patches/testing/project-accurate-backup.patch (révision 6410)
1382 +++ patches/testing/project-accurate-backup.patch (copie de travail)
1385 /* Commands sent to File daemon */
1386 static char backupcmd[] = "backup\n";
1387 -@@ -96,7 +97,443 @@
1388 +@@ -96,7 +97,450 @@
1396 ++bool db_get_jobids(JCR *jcr)
1401 +bool accurate_send_current_files(JCR *jcr)
1403 + char buf[MAXSTRING];
1405 + JOIN File USING (FileId)
1406 + WHERE File.FileIndex > 0
1408 ++ DROP TABLE btemp2
1410 +SELECT DISTINCT ON (PathId, FilenameId) FileIndex, Path, Name, LStat
1411 + FROM File JOIN Filename USING (FilenameId) JOIN Path USING (PathId) WHERE JobId IN (40341)
1412 @@ -299,6 +305,15 @@
1413 + char ed1[50], ed2[50];
1415 + bsnprintf(buf, sizeof(buf),
1416 ++ "SELECT Name FROM ToBackup%s",
1417 ++ edit_uint64(jcr->JobId, ed2));
1419 ++ /* missing_handler is called for each file found */
1420 ++ Dmsg1(2, "display files to backup cmd=%s\n", buf);
1421 ++ db_sql_query(jcr->db, buf, accurate_handler, (void *)jcr);
1422 ++ jcr->file_bsock->signal(BNET_EOD);
1424 ++ bsnprintf(buf, sizeof(buf),
1425 + "SELECT Path.Path,Filename.Name "
1426 + "FROM CurrentFile "
1427 + "JOIN File USING (FileId) "
1428 @@ -312,14 +327,6 @@
1429 + db_sql_query(jcr->db, buf, accurate_handler, (void *)jcr);
1430 + jcr->file_bsock->signal(BNET_EOD);
1432 -+ bsnprintf(buf, sizeof(buf),
1433 -+ "SELECT Name FROM ToBackup%s",
1434 -+ edit_uint64(jcr->JobId, ed2));
1435 -+ /* missing_handler is called for each file found */
1436 -+ Dmsg1(2, "display files to backup cmd=%s\n", buf);
1437 -+ db_sql_query(jcr->db, buf, accurate_handler, (void *)jcr);
1438 -+ jcr->file_bsock->signal(BNET_EOD);
1444 * Do a backup of the specified FileSet
1446 * Returns: false on failure
1447 -@@ -231,9 +668,18 @@
1448 +@@ -231,9 +675,18 @@
1452 @@ -538,18 +545,6 @@
1453 if (stat == JS_Terminated) {
1454 backup_cleanup(jcr, stat);
1456 -Index: src/dird/job.c
1457 -===================================================================
1458 ---- src/dird/job.c (révision 6372)
1459 -+++ src/dird/job.c (copie de travail)
1460 -@@ -979,6 +979,7 @@
1461 - jcr->spool_data = job->spool_data;
1462 - jcr->spool_size = job->spool_size;
1463 - jcr->write_part_after_job = job->write_part_after_job;
1464 -+ jcr->accurate = job->accurate;
1465 - if (jcr->RestoreBootstrap) {
1466 - free(jcr->RestoreBootstrap);
1467 - jcr->RestoreBootstrap = NULL;
1468 Index: src/dird/inc_conf.c
1469 ===================================================================
1470 --- src/dird/inc_conf.c (révision 6372)
1471 @@ -589,49 +584,15 @@
1475 -Index: src/dird/dird_conf.c
1476 -===================================================================
1477 ---- src/dird/dird_conf.c (révision 6372)
1478 -+++ src/dird/dird_conf.c (copie de travail)
1479 -@@ -319,6 +319,7 @@
1480 - {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
1481 - {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
1482 - {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
1483 -+ {"accurate", store_bool, ITEM(res_job.accurate), 0,0,0},
1484 - {NULL, NULL, {0}, 0, 0, 0}
1487 -@@ -618,6 +619,9 @@
1488 - if (res->res_job.spool_size) {
1489 - sendit(sock, _(" SpoolSize=%s\n"), edit_uint64(res->res_job.spool_size, ed1));
1491 -+ if (res->res_job.JobType == JT_BACKUP) {
1492 -+ sendit(sock, _(" Accurate=%d\n"), res->res_job.accurate);
1494 - if (res->res_job.JobType == JT_MIGRATE) {
1495 - sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
1497 -Index: src/dird/dird_conf.h
1498 -===================================================================
1499 ---- src/dird/dird_conf.h (révision 6372)
1500 -+++ src/dird/dird_conf.h (copie de travail)
1501 -@@ -400,6 +400,7 @@
1502 - bool write_part_after_job; /* Set to write part after job in SD */
1503 - bool enabled; /* Set if job enabled */
1504 - bool OptimizeJobScheduling; /* Set if we should optimize Job scheduling */
1505 -+ bool accurate; /* Set if it is an accurate backup job */
1507 - MSGS *messages; /* How and where to send messages */
1508 - SCHED *schedule; /* When -- Automatic schedule */
1509 Index: src/filed/backup.c
1510 ===================================================================
1511 --- src/filed/backup.c (révision 6372)
1512 +++ src/filed/backup.c (copie de travail)
1513 -@@ -48,8 +48,113 @@
1514 +@@ -48,8 +48,114 @@
1515 static bool crypto_session_start(JCR *jcr);
1516 static void crypto_session_end(JCR *jcr);
1517 static bool crypto_session_send(JCR *jcr, BSOCK *sd);
1518 -+static bool encode_and_send_deleted_files(JCR *jcr, char *fname);
1519 ++static bool encode_and_send_deleted_file(JCR *jcr, char *fname);
1522 + * Called by save_file when accept/discard file for backup
1523 @@ -704,16 +665,17 @@
1527 -+ /* get deleted files */
1528 -+ while (dir->recv() >= 0) {
1529 -+ Dmsg1(1, "deleted = %s\n", dir->msg);
1530 -+ encode_and_send_deleted_files(jcr, dir->msg);
1532 + /* get missing files */
1533 + while (dir->recv() >= 0) {
1534 + Dmsg1(1, "missing = %s\n", dir->msg);
1538 ++ /* get deleted files */
1539 ++ while (dir->recv() >= 0) {
1540 ++ Dmsg1(1, "deleted = %s\n", dir->msg);
1541 ++ encode_and_send_deleted_file(jcr, dir->msg);
1548 * Find all the requested files and send them
1549 * to the Storage daemon.
1555 // TODO landonf: Allow user to specify encryption algorithm
1557 sd = jcr->store_bsock;
1559 set_jcr_job_status(jcr, JS_Running);
1560 -@@ -134,6 +238,20 @@
1561 +@@ -134,6 +239,20 @@
1562 ok = false; /* error */
1563 set_jcr_job_status(jcr, JS_ErrorTerminated);
1567 free_pool_memory(jcr->acl_text);
1569 -@@ -355,9 +473,11 @@
1570 +@@ -355,9 +474,11 @@
1573 Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname);
1578 -@@ -1111,6 +1231,9 @@
1579 +@@ -1111,6 +1232,9 @@
1581 unstrip_path(ff_pkt);
1583 @@ -792,11 +754,11 @@
1584 Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
1586 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
1587 -@@ -1121,6 +1244,58 @@
1588 +@@ -1121,6 +1245,58 @@
1592 -+static bool encode_and_send_deleted_files(JCR *jcr, char *fname)
1593 ++static bool encode_and_send_deleted_file(JCR *jcr, char *fname)
1595 + BSOCK *sd = jcr->store_bsock;
1597 @@ -1402,18 +1364,6 @@
1599 /* Send attributes and digest to Director for Catalog */
1600 if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX ||
1602 -===================================================================
1603 ---- src/jcr.h (révision 6372)
1604 -+++ src/jcr.h (copie de travail)
1605 -@@ -208,6 +208,7 @@
1606 - B_DB *db_batch; /* database pointer for batch insert */
1607 - ATTR_DBR *ar; /* DB attribute record */
1608 - guid_list *id_list; /* User/group id to name list */
1609 -+ bool accurate; /* true if job is accurate */
1611 - void *plugin_ctx_list; /* list of contexts for plugins */
1612 - void *plugin_ctx; /* current plugin context */
1613 Index: src/findlib/find.h
1614 ===================================================================
1615 --- src/findlib/find.h (révision 6372)