X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fpatches%2Ftesting%2Fproject-accurate-backup.patch2;h=e90ae5a0ba1892fcb3fb10fea028177384aedc98;hb=78d36872abce7c3fc067945293559cb23666d4bc;hp=01e0c8b8efff44debc033738a96ff7caa7c04620;hpb=3b697541c8561c504ccdbb5e41f67ff961f6384f;p=bacula%2Fbacula diff --git a/bacula/patches/testing/project-accurate-backup.patch2 b/bacula/patches/testing/project-accurate-backup.patch2 index 01e0c8b8ef..e90ae5a0ba 100644 --- a/bacula/patches/testing/project-accurate-backup.patch2 +++ b/bacula/patches/testing/project-accurate-backup.patch2 @@ -1,6 +1,6 @@ Index: src/dird/fd_cmds.c =================================================================== ---- src/dird/fd_cmds.c (révision 6443) +--- src/dird/fd_cmds.c (révision 6467) +++ src/dird/fd_cmds.c (copie de travail) @@ -50,7 +50,7 @@ static char filesetcmd[] = "fileset%s\n"; /* set full fileset */ @@ -11,7 +11,7 @@ Index: src/dird/fd_cmds.c static char runscript[] = "Run OnSuccess=%u OnFailure=%u AbortOnError=%u When=%u Command=%s\n"; static char runbeforenow[]= "RunBeforeNow\n"; -@@ -226,7 +226,7 @@ +@@ -226,13 +226,12 @@ char ed1[50]; stime = str_to_utime(jcr->stime); @@ -20,24 +20,31 @@ Index: src/dird/fd_cmds.c while (bget_dirmsg(fd) >= 0) { /* allow him to poll us to sync clocks */ Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg); } -@@ -240,24 +240,25 @@ + } + +- + /* + * Send level command to FD. + * Used for backup jobs and estimate command. +@@ -240,24 +239,26 @@ bool send_level_command(JCR *jcr) { BSOCK *fd = jcr->file_bsock; + const char *accurate=jcr->job->accurate?"accurate_":""; ++ const char *not_accurate=""; /* * Send Level command to File daemon */ switch (jcr->JobLevel) { case L_BASE: - fd->fsend(levelcmd, "base", " ", 0); -+ fd->fsend(levelcmd, "", "base", " ", 0); ++ fd->fsend(levelcmd, not_accurate, "base", " ", 0); break; /* L_NONE is the console, sending something off to the FD */ case L_NONE: case L_FULL: - fd->fsend(levelcmd, "full", " ", 0); -+ fd->fsend(levelcmd, "", "full", " ", 0); ++ fd->fsend(levelcmd, not_accurate, "full", " ", 0); break; case L_DIFFERENTIAL: - fd->fsend(levelcmd, "differential", " ", 0); @@ -52,20 +59,14 @@ Index: src/dird/fd_cmds.c case L_SINCE: Index: src/dird/backup.c =================================================================== ---- src/dird/backup.c (révision 6443) +--- src/dird/backup.c (révision 6467) +++ src/dird/backup.c (copie de travail) -@@ -44,6 +44,7 @@ - #include "bacula.h" - #include "dird.h" - #include "ua.h" -+#include "findlib/find.h" - - /* Commands sent to File daemon */ - static char backupcmd[] = "backup\n"; -@@ -96,7 +97,93 @@ - return true; +@@ -97,6 +97,65 @@ } + /* ++ * Foreach files in currrent list, send "/path/fname\0LStat" to FD ++ */ +static int accurate_list_handler(void *ctx, int num_fields, char **row) +{ + JCR *jcr = (JCR *)ctx; @@ -74,81 +75,51 @@ Index: src/dird/backup.c + return 1; + } + -+ if (row[0] > 0) { /* discard when file_index == 0 */ -+ jcr->file_bsock->fsend("%s%s%c%s", row[1], row[2], 0, row[3]); ++ if (row[2] > 0) { /* discard when file_index == 0 */ ++ jcr->file_bsock->fsend("%s%s%c%s", row[0], row[1], 0, row[4]); + } + return 0; +} + -+/* Full : do nothing -+ * Differential : get the last full id -+ * Incremental : get the last full + last diff + last incr(s) ids -+ * -+ * TODO: look and merge from ua_restore.c ++/* ++ * Send current file list to FD ++ * DIR -> FD : accurate files=xxxx ++ * DIR -> FD : /path/to/file\0Lstat ++ * DIR -> FD : /path/to/dir/\0Lstat ++ * ... ++ * DIR -> FD : EOD + */ -+bool db_accurate_get_jobids(JCR *jcr, POOLMEM *jobids) -+{ -+ pm_strcpy(jobids, "42"); -+ return 1; -+} -+ +bool send_accurate_current_files(JCR *jcr) +{ -+ char buf[MAXSTRING]; -+ char ed1[50], ed2[50]; ++ POOL_MEM buf; + -+ if (jcr->accurate == false || job_canceled(jcr) || jcr->JobLevel == L_FULL) { ++ if (jcr->accurate==false || job_canceled(jcr) || jcr->JobLevel==L_FULL) { + return true; + } -+ + POOLMEM *jobids = get_pool_memory(PM_FNAME); -+ db_accurate_get_jobids(jcr, jobids); -+ -+ bsnprintf(buf, sizeof(buf), -+ "CREATE TEMPORARY TABLE btemp2%s AS ( " -+ "SELECT max(FileId) as FileId, PathId, FilenameId " -+ "FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (%s)) AS F " -+ "GROUP BY PathId, FilenameId ) ", -+ edit_uint64(jcr->JobId, ed1), -+ jobids); -+ db_sql_query(jcr->db, buf, NULL, NULL); -+ -+ bsnprintf(buf, sizeof(buf), "SELECT count(1) FROM btemp2%s",ed1); -+ db_sql_query(jcr->db, buf, NULL, NULL); // TODO: compter le nombre de rows -+ -+ jcr->file_bsock->fsend("accurate files=%s\n", edit_uint64(337969*2, ed2)); -+ -+ bsnprintf(buf, sizeof(buf), -+ "SELECT File.FileIndex, Path.Path, Filename.Name, File.LStat " -+ "FROM btemp2%s JOIN Path USING (PathId) JOIN Filename USING (FilenameId) " -+ "JOIN File USING (FileId) " -+ "WHERE File.FileIndex > 0", -+ ed1, jobids); -+ db_sql_query(jcr->db, buf, accurate_list_handler, (void *)jcr); -+ -+ bsnprintf(buf, sizeof(buf), "DROP TABLE btemp2%s", ed1); -+ free_pool_memory(jobids); ++ db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, jobids); + - /* -+ CREATE TEMPORARY TABLE btemp2 AS ( -+ SELECT max(FileId) as FileId, PathId, FilenameId -+ FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (39867,40341)) AS F -+ GROUP BY PathId, FilenameId ) -+ -+ SELECT File.FileIndex, Path.Path, Filename.Name, File.LStat -+ FROM btemp2 JOIN Path USING (PathId) JOIN Filename USING (FilenameId) -+ JOIN File USING (FileId) -+ WHERE File.FileIndex > 0 -+ -+ DROP TABLE btemp2 -+*/ -+/* -+SELECT DISTINCT ON (PathId, FilenameId) FileIndex, Path, Name, LStat -+ FROM File JOIN Filename USING (FilenameId) JOIN Path USING (PathId) WHERE JobId IN (40341) -+ ORDER BY PathId, FilenameId, JobId DESC -+*/ ++ if (*jobids == 0) { ++ free_pool_memory(jobids); ++ Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n")); ++ return false; ++ } ++ Jmsg(jcr, M_INFO, 0, _("Sending Accurate information.\n")); ++ ++ /* to be able to allocate the right size for htable */ ++ POOLMEM *nb = get_pool_memory(PM_FNAME); ++ Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)",jobids); ++ db_sql_query(jcr->db, buf.c_str(), db_get_int_handler, nb); ++ jcr->file_bsock->fsend("accurate files=%s\n", nb); ++ ++ db_get_file_list(jcr, jcr->db, jobids, accurate_list_handler, (void *)jcr); ++ ++ free_pool_memory(jobids); ++ free_pool_memory(nb); + + jcr->file_bsock->signal(BNET_EOD); ++ /* TODO: use response() ? */ ++ + return true; +} + @@ -156,13 +127,13 @@ Index: src/dird/backup.c * Do a backup of the specified FileSet * * Returns: false on failure -@@ -225,6 +312,14 @@ +@@ -225,6 +284,14 @@ Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db)); } + /* -+ * If backup is in accurate mode, FD will send the list of -+ * all files. ++ * If backup is in accurate mode, we send the list of ++ * all files to FD. + */ + if (!send_accurate_current_files(jcr)) { + goto bail_out; @@ -171,81 +142,136 @@ Index: src/dird/backup.c /* Send backup command */ fd->fsend(backupcmd); if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) { -@@ -234,6 +329,7 @@ - /* Pickup Job termination data */ - stat = wait_for_job_termination(jcr); - db_write_batch_file_records(jcr); /* used by bulk batch file insert */ -+ - if (stat == JS_Terminated) { - backup_cleanup(jcr, stat); - return true; -Index: src/dird/inc_conf.c +@@ -475,6 +542,7 @@ + " Software Compression: %s\n" + " VSS: %s\n" + " Encryption: %s\n" ++" Accurate: %s\n" + " Volume name(s): %s\n" + " Volume Session Id: %d\n" + " Volume Session Time: %d\n" +@@ -506,8 +574,9 @@ + edit_uint64_with_suffix(jcr->SDJobBytes, ec6), + kbps, + compress, +- jcr->VSS?"yes":"no", +- jcr->Encrypt?"yes":"no", ++ jcr->VSS?_("yes"):_("no"), ++ jcr->Encrypt?_("yes"):_("no"), ++ jcr->accurate?_("yes"):_("no"), + jcr->VolumeName, + jcr->VolSessionId, + jcr->VolSessionTime, +Index: src/dird/ua_restore.c =================================================================== ---- src/dird/inc_conf.c (révision 6443) -+++ src/dird/inc_conf.c (copie de travail) -@@ -94,6 +94,7 @@ - * Items that are valid in an Options resource - */ - static RES_ITEM options_items[] = { -+ {"accurate", store_opts, {0}, 0, 0, 0}, - {"compression", store_opts, {0}, 0, 0, 0}, - {"signature", store_opts, {0}, 0, 0, 0}, - {"verify", store_opts, {0}, 0, 0, 0}, -@@ -153,7 +154,8 @@ - INC_KW_NOATIME, - INC_KW_ENHANCEDWILD, - INC_KW_CHKCHANGES, -- INC_KW_STRIPPATH -+ INC_KW_STRIPPATH, -+ INC_KW_ACCURATE - }; +--- src/dird/ua_restore.c (révision 6467) ++++ src/dird/ua_restore.c (copie de travail) +@@ -1005,7 +1005,6 @@ + * For display purposes, the same JobId, with different volumes may + * appear more than once, however, we only insert it once. + */ +- int items = 0; + p = rx->JobIds; + tree.FileEstimate = 0; + if (get_next_jobid_from_list(&p, &JobId) > 0) { +@@ -1020,23 +1019,12 @@ + tree.DeltaCount = rx->JobId/50; /* print 50 ticks */ + } + } +- for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) { +- char ed1[50]; - /* -@@ -163,6 +165,7 @@ - * options given above. - */ - static struct s_kw FS_option_kw[] = { -+ {"accurate", INC_KW_ACCURATE}, - {"compression", INC_KW_COMPRESSION}, - {"signature", INC_KW_DIGEST}, - {"encryption", INC_KW_ENCRYPTION}, -@@ -251,6 +254,8 @@ - {"no", INC_KW_ENHANCEDWILD, "0"}, - {"yes", INC_KW_CHKCHANGES, "c"}, - {"no", INC_KW_CHKCHANGES, "0"}, -+ {"yes", INC_KW_ACCURATE, "C"}, -+ {"no", INC_KW_ACCURATE, "0"}, - {NULL, 0, 0} - }; +- if (JobId == last_JobId) { +- continue; /* eliminate duplicate JobIds */ +- } +- last_JobId = JobId; +- ua->info_msg(_("\nBuilding directory tree for JobId %s ... "), +- edit_int64(JobId, ed1)); +- items++; +- /* +- * Find files for this JobId and insert them in the tree +- */ +- Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1)); +- if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) { +- ua->error_msg("%s", db_strerror(ua->db)); +- } ++ ua->info_msg(_("\nBuilding directory tree for JobId(s) %s ... "), ++ rx->JobIds); ++ ++ if (!db_get_file_list(ua->jcr, ua->db, rx->JobIds, insert_tree_handler, (void *)&tree)) { ++ ua->error_msg("%s", db_strerror(ua->db)); + } + if (tree.FileCount == 0) { + ua->send_msg(_("\nThere were no files inserted into the tree, so file selection\n" +@@ -1055,26 +1043,13 @@ + } + } else { + char ec1[50]; +- if (items==1) { +- if (tree.all) { +- ua->info_msg(_("\n1 Job, %s files inserted into the tree and marked for extraction.\n"), +- edit_uint64_with_commas(tree.FileCount, ec1)); +- } +- else { +- ua->info_msg(_("\n1 Job, %s files inserted into the tree.\n"), +- edit_uint64_with_commas(tree.FileCount, ec1)); +- } ++ if (tree.all) { ++ ua->info_msg(_("\n%s files inserted into the tree and marked for extraction.\n"), ++ edit_uint64_with_commas(tree.FileCount, ec1)); ++ } else { ++ ua->info_msg(_("\n%s files inserted into the tree.\n"), ++ edit_uint64_with_commas(tree.FileCount, ec1)); + } +- else { +- if (tree.all) { +- ua->info_msg(_("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"), +- items, edit_uint64_with_commas(tree.FileCount, ec1)); +- } +- else { +- ua->info_msg(_("\n%d Jobs, %s files inserted into the tree.\n"), +- items, edit_uint64_with_commas(tree.FileCount, ec1)); +- } +- } + if (find_arg(ua, NT_("done")) < 0) { + /* Let the user interact in selecting which files to restore */ Index: src/filed/backup.c =================================================================== ---- src/filed/backup.c (révision 6443) +--- src/filed/backup.c (révision 6467) +++ src/filed/backup.c (copie de travail) -@@ -48,8 +48,220 @@ +@@ -37,6 +37,7 @@ + + #include "bacula.h" + #include "filed.h" ++#include "lib/htable.h" + + /* Forward referenced functions */ + int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level); +@@ -48,8 +49,231 @@ static bool crypto_session_start(JCR *jcr); static void crypto_session_end(JCR *jcr); static bool crypto_session_send(JCR *jcr, BSOCK *sd); +static bool encode_and_send_deleted_file(JCR *jcr, char *fname); -+#include "lib/htable.c" +typedef struct CurFile { + char *fname; + char *lstat; + hlink link; +} CurFile; ++ ++#define accurate_mark_file_as_seen(elt) ((elt)->lstat[0] = 0) ++#define accurate_file_has_been_seen(elt) ((elt)->lstat[0] == 0) + /* + * This function is called for each file seen in fileset. -+ * -+ * If the file is skipped (saved=false), we will check if this -+ * file have been backuped before. If not, we decide to backup it. -+ * -+ * If the file have saved=true, we mark it as seen ++ * We check in file_list hash if fname have been backuped ++ * the last time. After we can compare Lstat field. When ++ * a file have been seen, we put '\0' in LStat field. + * + */ -+/* TODO: tweak verify code to use the same function */ -+bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt, bool saved) ++/* TODO: tweak verify code to use the same function ?? */ ++bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt) +{ + char *p; + int stat=false; @@ -260,21 +286,19 @@ Index: src/filed/backup.c + return true; + } + -+ if (ff_pkt->type == FT_DIREND || ff_pkt->type == FT_REPARSE || ff_pkt->type == FT_DIRNOCHG) { ++ if (S_ISDIR(ff_pkt->statp.st_mode)) { + fname = ff_pkt->link; + } + + elt = (CurFile *) jcr->file_list->lookup(fname); + + if (!elt) { -+ // TODO: we must backup it ! -+ Dmsg1(1, "accurate %s = yes (not found)\n", fname); ++ Dmsg1(500, "accurate %s = yes (not found)\n", fname); + return true; + } + -+ if (saved || *elt->lstat == '\0') { -+ Dmsg1(1, "accurate %s = no (already seen)\n", fname); -+ *elt->lstat = '\0'; ++ if (accurate_file_has_been_seen(elt)) { ++ Dmsg1(500, "accurate %s = no (already seen)\n", fname); + return false; + } + @@ -285,102 +309,105 @@ Index: src/filed/backup.c + char ed1[30], ed2[30]; + switch (*p) { + case 'i': /* compare INODEs */ -+ if (statc.st_ino != ff_pkt->statp.st_ino) { -+ Jmsg(jcr, M_INFO, 0, _(" st_ino differ. Cat: %s File: %s\n"), -+ edit_uint64((uint64_t)statc.st_ino, ed1), -+ edit_uint64((uint64_t)ff_pkt->statp.st_ino, ed2)); -+ stat = true; -+ } -+ break; ++ if (statc.st_ino != ff_pkt->statp.st_ino) { ++ Jmsg(jcr, M_SAVED, 0, _("%s st_ino differ. Cat: %s File: %s\n"), fname, ++ edit_uint64((uint64_t)statc.st_ino, ed1), ++ edit_uint64((uint64_t)ff_pkt->statp.st_ino, ed2)); ++ stat = true; ++ } ++ break; + case 'p': /* permissions bits */ -+ if (statc.st_mode != ff_pkt->statp.st_mode) { -+ Jmsg(jcr, M_INFO, 0, _(" st_mode differ. Cat: %x File: %x\n"), -+ (uint32_t)statc.st_mode, (uint32_t)ff_pkt->statp.st_mode); -+ stat = true; -+ } -+ break; -+ case 'n': /* number of links */ -+ if (statc.st_nlink != ff_pkt->statp.st_nlink) { -+ Jmsg(jcr, M_INFO, 0, _(" st_nlink differ. Cat: %d File: %d\n"), -+ (uint32_t)statc.st_nlink, (uint32_t)ff_pkt->statp.st_nlink); -+ stat = true; -+ } -+ break; ++ if (statc.st_mode != ff_pkt->statp.st_mode) { ++ Jmsg(jcr, M_SAVED, 0, _("%s st_mode differ. Cat: %x File: %x\n"), fname, ++ (uint32_t)statc.st_mode, (uint32_t)ff_pkt->statp.st_mode); ++ stat = true; ++ } ++ break; ++// case 'n': /* number of links */ ++// if (statc.st_nlink != ff_pkt->statp.st_nlink) { ++// Jmsg(jcr, M_SAVED, 0, _("%s st_nlink differ. Cat: %d File: %d\n"), fname, ++// (uint32_t)statc.st_nlink, (uint32_t)ff_pkt->statp.st_nlink); ++// stat = true; ++// } ++// break; + case 'u': /* user id */ -+ if (statc.st_uid != ff_pkt->statp.st_uid) { -+ Jmsg(jcr, M_INFO, 0, _(" st_uid differ. Cat: %u File: %u\n"), -+ (uint32_t)statc.st_uid, (uint32_t)ff_pkt->statp.st_uid); -+ stat = true; -+ } -+ break; ++ if (statc.st_uid != ff_pkt->statp.st_uid) { ++ Jmsg(jcr, M_SAVED, 0, _("%s st_uid differ. Cat: %u File: %u\n"), fname, ++ (uint32_t)statc.st_uid, (uint32_t)ff_pkt->statp.st_uid); ++ stat = true; ++ } ++ break; + case 'g': /* group id */ -+ if (statc.st_gid != ff_pkt->statp.st_gid) { -+ Jmsg(jcr, M_INFO, 0, _(" st_gid differ. Cat: %u File: %u\n"), -+ (uint32_t)statc.st_gid, (uint32_t)ff_pkt->statp.st_gid); -+ stat = true; -+ } -+ break; ++ if (statc.st_gid != ff_pkt->statp.st_gid) { ++ Jmsg(jcr, M_SAVED, 0, _("%s st_gid differ. Cat: %u File: %u\n"), fname, ++ (uint32_t)statc.st_gid, (uint32_t)ff_pkt->statp.st_gid); ++ stat = true; ++ } ++ break; + case 's': /* size */ -+ if (statc.st_size != ff_pkt->statp.st_size) { -+ Jmsg(jcr, M_INFO, 0, _(" st_size differ. Cat: %s File: %s\n"), -+ edit_uint64((uint64_t)statc.st_size, ed1), -+ edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2)); -+ stat = true; -+ } -+ break; -+ case 'a': /* access time */ -+ if (statc.st_atime != ff_pkt->statp.st_atime) { -+ Jmsg(jcr, M_INFO, 0, _(" st_atime differs\n")); -+ stat = true; -+ } -+ break; ++ if (statc.st_size != ff_pkt->statp.st_size) { ++ Jmsg(jcr, M_SAVED, 0, _("%s st_size differ. Cat: %s File: %s\n"), fname, ++ edit_uint64((uint64_t)statc.st_size, ed1), ++ edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2)); ++ stat = true; ++ } ++ break; ++// case 'a': /* access time */ ++// if (statc.st_atime != ff_pkt->statp.st_atime) { ++// Jmsg(jcr, M_SAVED, 0, _("%s st_atime differs\n"), fname); ++// stat = true; ++// } ++// break; + case 'm': -+ if (statc.st_mtime != ff_pkt->statp.st_mtime) { -+ Jmsg(jcr, M_INFO, 0, _(" st_mtime differs\n")); -+ stat = true; -+ } -+ break; ++ if (statc.st_mtime != ff_pkt->statp.st_mtime) { ++ Jmsg(jcr, M_SAVED, 0, _("%s st_mtime differs\n"), fname); ++ stat = true; ++ } ++ break; + case 'c': /* ctime */ -+ if (statc.st_ctime != ff_pkt->statp.st_ctime) { -+ Jmsg(jcr, M_INFO, 0, _(" st_ctime differs\n")); -+ stat = true; -+ } -+ break; ++ if (statc.st_ctime != ff_pkt->statp.st_ctime) { ++ Jmsg(jcr, M_SAVED, 0, _("%s st_ctime differs\n"), fname); ++ stat = true; ++ } ++ break; + case 'd': /* file size decrease */ -+ if (statc.st_size > ff_pkt->statp.st_size) { -+ Jmsg(jcr, M_INFO, 0, _(" st_size decrease. Cat: %s File: %s\n"), -+ edit_uint64((uint64_t)statc.st_size, ed1), -+ edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2)); -+ stat = true; -+ } -+ break; ++ if (statc.st_size > ff_pkt->statp.st_size) { ++ Jmsg(jcr, M_SAVED, 0, _("%s st_size decrease. Cat: %s File: %s\n"), fname, ++ edit_uint64((uint64_t)statc.st_size, ed1), ++ edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2)); ++ stat = true; ++ } ++ break; + case '5': /* compare MD5 */ -+ Dmsg1(500, "set Do_MD5 for %s\n", ff_pkt->fname); -+// *do_Digest = CRYPTO_DIGEST_MD5; -+ break; ++ Dmsg1(500, "set Do_MD5 for %s\n", ff_pkt->fname); ++// *do_Digest = CRYPTO_DIGEST_MD5; ++ break; + case '1': /* compare SHA1 */ -+// *do_Digest = CRYPTO_DIGEST_SHA1; -+ break; ++// *do_Digest = CRYPTO_DIGEST_SHA1; ++ break; + case ':': + case 'V': + default: -+ break; ++ break; + } + } -+ *elt->lstat = '\0'; /* mark it as seen */ -+ Dmsg2(1, "accurate %s = %i\n", fname, stat); ++ accurate_mark_file_as_seen(elt); ++ Dmsg2(500, "accurate %s = %i\n", fname, stat); + return stat; +} + -+static int deb=0; -+int accurate_get_current_file_list_cmd(JCR *jcr) ++/* ++ * This function doesn't work very well with smartalloc ++ * TODO: use bigbuffer from htable ++ */ ++int accurate_cmd(JCR *jcr) +{ + BSOCK *dir = jcr->dir_bsock; + int len; + uint64_t nb; + CurFile *elt=NULL; + -+ if (jcr->accurate == false || job_canceled(jcr) || jcr->JobLevel == L_FULL) { ++ if (jcr->accurate==false || job_canceled(jcr) || jcr->JobLevel==L_FULL) { + return true; + } + @@ -388,54 +415,64 @@ Index: src/filed/backup.c + dir->fsend(_("2991 Bad accurate command\n")); + return false; + } -+ ++ + jcr->file_list = (htable *)malloc(sizeof(htable)); + jcr->file_list->init(elt, &elt->link, nb); + ++ /* ++ * buffer = sizeof(CurFile) + dirmsg ++ * dirmsg = fname + lstat ++ */ + /* get current files */ + while (dir->recv() >= 0) { + len = strlen(dir->msg); + if ((len+1) < dir->msglen) { -+ elt = (CurFile *)malloc(sizeof(CurFile)); -+ elt->fname = (char *) malloc(dir->msglen+1); -+ memcpy(elt->fname, dir->msg, dir->msglen); -+ elt->fname[dir->msglen]='\0'; -+ elt->lstat = elt->fname + len + 1; -+ if ((deb % 1000) == 1) { -+ Dmsg1(1, "deb=%i\n", deb); -+ } -+ if ((deb % 5000) == 1) { -+ jcr->file_list->stats(); -+ } -+ Dmsg2(100, "hash[%s]=%s\n", elt->fname, elt->lstat); -+ jcr->file_list->insert(elt->fname, elt); -+ deb++; ++// elt = (CurFile *)malloc(sizeof(CurFile)); ++// elt->fname = (char *) malloc(dir->msglen+1); ++ ++ /* we store CurFile, fname and lstat in the same chunk */ ++ elt = (CurFile *)malloc(sizeof(CurFile)+dir->msglen+1); ++ elt->fname = (char *) elt+sizeof(CurFile); ++ memcpy(elt->fname, dir->msg, dir->msglen); ++ elt->fname[dir->msglen]='\0'; ++ elt->lstat = elt->fname + len + 1; ++ jcr->file_list->insert(elt->fname, elt); ++ Dmsg2(500, "add fname=%s lstat=%s\n", elt->fname, elt->lstat); + } + } -+ -+ jcr->file_list->stats(); -+// dir->fsend("2000 OK accurate\n"); + ++// jcr->file_list->stats(); ++ /* TODO: send a EOM ? ++ dir->fsend("2000 OK accurate\n"); ++ */ + return true; +} + +bool accurate_send_deleted_list(JCR *jcr) +{ + if (jcr->accurate == false || jcr->JobLevel == L_FULL) { -+ return true; ++ goto bailout; ++ } ++ ++ if (jcr->file_list == NULL) { ++ goto bailout; + } + + CurFile *elt; + foreach_htable (elt, jcr->file_list) { -+ Dmsg3(100, "elt = 0x%x fname=%s lstat=%s\n", elt, elt->fname, elt->lstat); -+ if (*elt->lstat != '\0') { -+ encode_and_send_deleted_file(jcr, elt->fname); ++ if (accurate_file_has_been_seen(elt)) { /* already seen */ ++ Dmsg2(500, "deleted fname=%s lstat=%s\n", elt->fname, elt->lstat); ++ encode_and_send_deleted_file(jcr, elt->fname); + } -+ free(elt->fname); ++// free(elt->fname); ++ } ++bailout: ++ /* TODO: clean htable when this function is not reached ? */ ++ if (jcr->file_list) { ++ jcr->file_list->destroy(); ++ free(jcr->file_list); ++ jcr->file_list = NULL; + } -+ jcr->file_list->destroy(); /* TODO: clean htable when this function is not reached ? */ -+ free(jcr->file_list); -+ jcr->file_list = NULL; + return true; +} + @@ -443,56 +480,41 @@ Index: src/filed/backup.c * Find all the requested files and send them * to the Storage daemon. * -@@ -66,7 +278,6 @@ - BSOCK *sd; - bool ok = true; - // TODO landonf: Allow user to specify encryption algorithm +@@ -100,7 +324,7 @@ + */ + jcr->compress_buf_size = jcr->buf_size + ((jcr->buf_size+999) / 1000) + 30; + jcr->compress_buf = get_memory(jcr->compress_buf_size); - - sd = jcr->store_bsock; ++ + #ifdef HAVE_LIBZ + z_stream *pZlibStream = (z_stream*)malloc(sizeof(z_stream)); + if (pZlibStream) { +@@ -121,10 +345,13 @@ + return false; + } - set_jcr_job_status(jcr, JS_Running); -@@ -134,7 +345,10 @@ - ok = false; /* error */ +- Dmsg1(300, "set_find_options ff=%p\n", jcr->ff); + set_find_options((FF_PKT *)jcr->ff, jcr->incremental, jcr->mtime); +- Dmsg0(300, "start find files\n"); + ++ /* in accurate mode, we overwrite the find_one check function */ ++ if (jcr->accurate) { ++ set_find_changed_function((FF_PKT *)jcr->ff, accurate_check_file); ++ } ++ + start_heartbeat_monitor(jcr); + + jcr->acl_text = get_pool_memory(PM_MESSAGE); +@@ -135,6 +362,8 @@ set_jcr_job_status(jcr, JS_ErrorTerminated); } -+ Dmsg1(1, "jcr->accurate == %i\n", jcr->accurate); + accurate_send_deleted_list(jcr); /* send deleted list to SD */ + free_pool_memory(jcr->acl_text); stop_heartbeat_monitor(jcr); -@@ -354,9 +568,19 @@ - } - case FT_DIRNOCHG: - case FT_NOCHG: -+ /* TODO: in accurate mode, we have to change NOCHG attribute to FT_REG... */ -+// if (!accurate_check_file(jcr, ff_pkt, false)) { -+// Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname); -+// return 1; -+// } - Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname); - return 1; - case FT_ISARCH: -+ /* TODO: in accurate mode, we have to change NOCHG attribute to FT_REG... */ -+// if (!accurate_check_file(jcr, ff_pkt, false)) { -+// Jmsg(jcr, M_NOTSAVED, 0, _(" Archive file not saved: %s\n"), ff_pkt->fname); -+// return 1; -+// } - Jmsg(jcr, M_NOTSAVED, 0, _(" Archive file not saved: %s\n"), ff_pkt->fname); - return 1; - case FT_NOOPEN: { -@@ -1118,6 +1342,9 @@ - } - unstrip_path(ff_pkt); - -+ /* list backuped files */ -+ accurate_check_file(jcr, ff_pkt, true); -+ - Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg); - if (!stat) { - Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), -@@ -1128,6 +1355,58 @@ +@@ -1128,6 +1357,57 @@ return true; } @@ -532,11 +554,10 @@ Index: src/filed/backup.c + * For a directory, link is the same as fname, but with trailing + * slash. For a linked file, link is the link. + */ -+ stat = sd->fsend("%ld %d %s%c%s%c%s%c%s%c", -+ 0 /* FileIndex */, -+ FT_NOSTAT /* FileType */, -+ fname /* FileName */, -+ 0, attribs, 0, 0, 0, attribsEx, 0); ++ stat = sd->fsend("0 %d %s%c%s%c%s%c%s%c", ++ FT_NOSTAT /* FileType */, ++ fname /* FileName */, ++ 0, attribs, 0, 0, 0, attribsEx, 0); + + Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg); + if (!stat) { @@ -553,13 +574,13 @@ Index: src/filed/backup.c */ Index: src/filed/job.c =================================================================== ---- src/filed/job.c (révision 6443) +--- src/filed/job.c (révision 6467) +++ src/filed/job.c (copie de travail) @@ -49,6 +49,7 @@ /* Imported functions */ extern int status_cmd(JCR *jcr); extern int qstatus_cmd(JCR *jcr); -+extern int accurate_get_current_file_list_cmd(JCR *jcr); ++extern int accurate_cmd(JCR *jcr); /* Forward referenced functions */ static int backup_cmd(JCR *jcr); @@ -567,21 +588,11 @@ Index: src/filed/job.c {"RunBeforeJob", runbefore_cmd, 0}, {"RunAfterJob", runafter_cmd, 0}, {"Run", runscript_cmd, 0}, -+ {"accurate", accurate_get_current_file_list_cmd, 0}, ++ {"accurate", accurate_cmd, 0}, {NULL, NULL} /* list terminator */ }; -@@ -1087,6 +1089,9 @@ - case 'c': - fo->flags |= FO_CHKCHANGES; - break; -+ case 'C': -+ fo->flags |= FO_ACCURATE; -+ break; - default: - Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p); - break; -@@ -1195,6 +1200,9 @@ +@@ -1195,6 +1197,9 @@ level = get_memory(dir->msglen+1); Dmsg1(110, "level_cmd: %s", dir->msg); @@ -591,7 +602,7 @@ Index: src/filed/job.c if (sscanf(dir->msg, "level = %s ", level) != 1) { goto bail_out; } -@@ -1204,14 +1212,14 @@ +@@ -1204,14 +1209,14 @@ /* Full backup requested? */ } else if (strcmp(level, "full") == 0) { jcr->JobLevel = L_FULL; @@ -611,7 +622,7 @@ Index: src/filed/job.c * to agree with our clock. Index: src/filed/restore.c =================================================================== ---- src/filed/restore.c (révision 6443) +--- src/filed/restore.c (révision 6467) +++ src/filed/restore.c (copie de travail) @@ -320,6 +320,11 @@ bclose(&rctx.bfd); @@ -625,9 +636,178 @@ Index: src/filed/restore.c /* * Unpack attributes and do sanity check them */ +Index: src/cats/protos.h +=================================================================== +--- src/cats/protos.h (révision 6467) ++++ src/cats/protos.h (copie de travail) +@@ -102,6 +102,9 @@ + int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cdbr); + int db_get_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr); + bool db_get_query_dbids(JCR *jcr, B_DB *mdb, POOL_MEM &query, dbid_list &ids); ++bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids, DB_RESULT_HANDLER *result_handler, void *ctx); ++bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM *jobids); ++int db_get_int_handler(void *ctx, int num_fields, char **row); + + + /* sql_list.c */ +Index: src/cats/sql_get.c +=================================================================== +--- src/cats/sql_get.c (révision 6467) ++++ src/cats/sql_get.c (copie de travail) +@@ -898,8 +898,6 @@ + return ok; + } + +- +- + /* Get Media Record + * + * Returns: false: on failure +@@ -1018,5 +1016,141 @@ + return ok; + } + ++/* ++ * Find the last "accurate" backup state (that can take deleted files in account) ++ * 1) Get all files with jobid in list (F subquery) ++ * 2) Take only the last version of each file (Temp subquery) => accurate list is ok ++ * 3) Join the result to file table to get fileindex, jobid and lstat information ++ * ++ * TODO: On postgresql, this is done with ++SELECT DISTINCT ON (PathId, FilenameId) FileIndex, Path, Name, LStat ++ FROM File JOIN Filename USING (FilenameId) JOIN Path USING (PathId) WHERE JobId IN (40341) ++ ORDER BY PathId, FilenameId, JobId DESC ++ */ ++bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids, ++ DB_RESULT_HANDLER *result_handler, void *ctx) ++{ ++ if (!*jobids) { ++ db_lock(mdb); ++ Mmsg(mdb->errmsg, _("ERR=JobIds are empty\n")); ++ db_unlock(mdb); ++ return false; ++ } + ++ POOL_MEM buf (PM_MESSAGE); ++ ++ Mmsg(buf, ++ "SELECT Path.Path, Filename.Name, File.FileIndex, File.JobId, File.LStat " ++ "FROM ( " ++ "SELECT max(FileId) as FileId, PathId, FilenameId " ++ "FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (%s)) AS F " ++ "GROUP BY PathId, FilenameId " ++ ") AS Temp " ++ "JOIN Filename ON (Filename.FilenameId = Temp.FilenameId) " ++ "JOIN Path ON (Path.PathId = Temp.PathId) " ++ "JOIN File ON (File.FileId = Temp.FileId) " ++ "WHERE File.FileIndex > 0 ", ++ jobids); ++ ++ return db_sql_query(mdb, buf.c_str(), result_handler, ctx); ++} ++ ++ ++/* Full : do nothing ++ * Differential : get the last full id ++ * Incremental : get the last full + last diff + last incr(s) ids ++ * ++ * TODO: look and merge from ua_restore.c ++ */ ++bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, ++ JOB_DBR *jr, POOLMEM *jobids) ++{ ++ char clientid[50], jobid[50], filesetid[50]; ++ char date[MAX_TIME_LENGTH]; ++ ++ POOL_MEM query (PM_FNAME); ++ bstrutime(date, sizeof(date), time(NULL) + 1); ++ jobids[0]='\0'; ++ ++ /* First, find the last good Full backup for this job/client/fileset */ ++ Mmsg(query, ++"CREATE TEMPORARY TABLE btemp3%s AS " ++ "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles " ++ "FROM Job JOIN FileSet USING (FileSetId) " ++ "WHERE ClientId = %s " ++ "AND Level='F' AND JobStatus='T' AND Type='B' " ++ "AND StartTime<'%s' " ++ "AND FileSet.FileSet=(SELECT FileSet FROM FileSet WHERE FileSetId = %s) " ++ "ORDER BY Job.JobTDate DESC LIMIT 1", ++ edit_uint64(jcr->JobId, jobid), ++ edit_uint64(jr->ClientId, clientid), ++ date, ++ edit_uint64(jr->FileSetId, filesetid)); ++ ++ if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) { ++ return false; ++ } ++ ++ if (jr->JobLevel == L_INCREMENTAL) { ++ ++ /* Now, find the last differential backup after the last full */ ++ Mmsg(query, ++"INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) " ++ "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles " ++ "FROM Job JOIN FileSet USING (FileSetId) " ++ "WHERE ClientId = %s " ++ "AND Level='D' AND JobStatus='T' AND Type='B' " ++ "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) " ++ "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) " ++ "ORDER BY Job.JobTDate DESC LIMIT 1 ", ++ jobid, ++ clientid, ++ jobid, ++ filesetid); ++ ++ db_sql_query(mdb, query.c_str(), NULL, NULL); ++ ++ /* We just have to take all incremental after the last Full/Diff */ ++ Mmsg(query, ++"INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) " ++ "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles " ++ "FROM Job JOIN FileSet USING (FileSetId) " ++ "WHERE ClientId = %s " ++ "AND Level='I' AND JobStatus='T' AND Type='B' " ++ "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) " ++ "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) " ++ "ORDER BY Job.JobTDate DESC ", ++ jobid, ++ clientid, ++ jobid, ++ filesetid); ++ db_sql_query(mdb, query.c_str(), NULL, NULL); ++ } ++ ++ /* build a jobid list ie: 1,2,3,4 */ ++ Mmsg(query, "SELECT JobId FROM btemp3%s", jobid); ++ db_sql_query(mdb, query.c_str(), db_get_int_handler, jobids); ++ Dmsg1(1, "db_accurate_get_jobids=%s\n", jobids); ++ ++ Mmsg(query, "DROP TABLE btemp3%s", jobid); ++ db_sql_query(mdb, query.c_str(), NULL, NULL); ++ ++ return true; ++} ++ ++/* ++ * Use to build a string of int list from a query. "10,20,30" ++ */ ++int db_get_int_handler(void *ctx, int num_fields, char **row) ++{ ++ POOLMEM *ret = (POOLMEM *)ctx; ++ if (num_fields == 1) { ++ if (ret[0]) { ++ pm_strcat(ret, ","); ++ } ++ pm_strcat(ret, row[0]); ++ } ++ return 0; ++} ++ + #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI */ Index: src/stored/bextract.c =================================================================== ---- src/stored/bextract.c (révision 6443) +--- src/stored/bextract.c (révision 6467) +++ src/stored/bextract.c (copie de travail) @@ -324,6 +324,14 @@ Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n")); @@ -636,9 +816,9 @@ Index: src/stored/bextract.c + /* handle deleted file + */ + if (rec->FileIndex == 0) { -+ /* if file is included, remove it ? */ -+ Jmsg(jcr, M_INFO, 0, _("fname=%s is marked as deleted.\n", attr->fname)); -+ break; ++ /* if file is included, remove it ? */ ++ Jmsg(jcr, M_INFO, 0, _("fname=%s is marked as deleted.\n"), attr->fname); ++ break; + } + if (attr->file_index != rec->FileIndex) { @@ -646,19 +826,19 @@ Index: src/stored/bextract.c rec->FileIndex, attr->file_index); Index: src/stored/bscan.c =================================================================== ---- src/stored/bscan.c (révision 6443) +--- src/stored/bscan.c (révision 6467) +++ src/stored/bscan.c (copie de travail) -@@ -648,6 +648,15 @@ +@@ -660,6 +660,15 @@ case STREAM_UNIX_ATTRIBUTES: case STREAM_UNIX_ATTRIBUTES_EX: + /* handle deleted file + */ + if (rec->FileIndex == 0) { -+ create_file_attributes_record(db, mjcr, attr->fname, attr->lname, -+ FT_NOSTAT, "", rec); -+ free_jcr(mjcr); -+ break; ++ create_file_attributes_record(db, mjcr, attr->fname, attr->lname, ++ FT_NOSTAT, "", rec); ++ free_jcr(mjcr); ++ break; + } + if (!unpack_attributes_record(bjcr, rec->Stream, rec->data, attr)) { @@ -666,18 +846,19 @@ Index: src/stored/bscan.c } Index: src/stored/append.c =================================================================== ---- src/stored/append.c (révision 6443) +--- src/stored/append.c (révision 6467) +++ src/stored/append.c (copie de travail) -@@ -146,7 +146,7 @@ +@@ -146,7 +146,8 @@ /* Read Stream header from the File daemon. * The stream header consists of the following: - * file_index (sequential Bacula file index, base 1) -+ * file_index (sequential Bacula file index, base 1, 0 for deleted files) ++ * file_index (sequential Bacula file index, base 1, ++ * 0 for deleted files) * stream (Bacula number to distinguish parts of data) * info (Info for Storage daemon -- compressed, encryped, ...) * info is not currently used, so is read, but ignored! -@@ -185,16 +185,18 @@ +@@ -185,16 +186,21 @@ Dmsg2(890, " 0 && (file_index == last_file_index || -+ file_index == last_file_index + 1))) { -+ Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n")); -+ ok = false; -+ break; -+ } -+ if (file_index != last_file_index) { -+ jcr->JobFiles = file_index; -+ last_file_index = file_index; -+ } ++ /* ++ * In accurate mode, files with file_index == 0 are marked as deleted ++ */ ++ if (!file_index) { ++ if (!(file_index > 0 && (file_index == last_file_index || ++ file_index == last_file_index + 1))) { ++ Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n")); ++ ok = false; ++ break; ++ } ++ if (file_index != last_file_index) { ++ jcr->JobFiles = file_index; ++ last_file_index = file_index; ++ } } - if (file_index != last_file_index) { - jcr->JobFiles = file_index; @@ -705,20 +889,29 @@ Index: src/stored/append.c /* Read data stream from the File daemon. * The data stream is just raw bytes -@@ -212,25 +214,26 @@ - stream_to_ascii(buf1, rec.Stream,rec.FileIndex), - rec.data_len); +@@ -214,22 +220,23 @@ -- while (!write_record_to_block(dcr->block, &rec)) { -- Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len, + while (!write_record_to_block(dcr->block, &rec)) { + Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len, - rec.remainder); -- if (!write_block_to_device(dcr)) { -- Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n", ++ rec.remainder); + if (!write_block_to_device(dcr)) { + Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n", - dev->print_name(), dev->bstrerror()); -- ok = false; -- break; -- } -- } ++ dev->print_name(), dev->bstrerror()); + ok = false; + break; + } ++ ++ if (!ok) { ++ Dmsg0(400, "Not OK\n"); ++ break; ++ } ++ jcr->JobBytes += rec.data_len; /* increment bytes this job */ ++ Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n", ++ FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId, ++ stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len); + } - if (!ok) { - Dmsg0(400, "Not OK\n"); - break; @@ -727,32 +920,12 @@ Index: src/stored/append.c - Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n", - FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId, - stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len); -+ while (!write_record_to_block(dcr->block, &rec)) { -+ Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len, -+ rec.remainder); -+ if (!write_block_to_device(dcr)) { -+ Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n", -+ dev->print_name(), dev->bstrerror()); -+ ok = false; -+ break; -+ } - -+ if (!ok) { -+ Dmsg0(400, "Not OK\n"); -+ break; -+ } -+ jcr->JobBytes += rec.data_len; /* increment bytes this job */ -+ Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n", -+ FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId, -+ stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len); -+ } -+ + /* Send attributes and digest to Director for Catalog */ if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX || - crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) { Index: src/jcr.h =================================================================== ---- src/jcr.h (révision 6443) +--- src/jcr.h (révision 6467) +++ src/jcr.h (copie de travail) @@ -119,6 +119,7 @@ @@ -770,15 +943,146 @@ Index: src/jcr.h #endif /* FILE_DAEMON */ +Index: src/lib/Makefile.in +=================================================================== +--- src/lib/Makefile.in (révision 6467) ++++ src/lib/Makefile.in (copie de travail) +@@ -29,7 +29,7 @@ + res.c rwlock.c scan.c serial.c sha1.c \ + signal.c smartall.c rblist.c tls.c tree.c \ + util.c var.c watchdog.c workq.c btimers.c \ +- address_conf.c pythonlib.c breg.c ++ address_conf.c pythonlib.c breg.c htable.c + + + LIBOBJS = attr.o base64.o berrno.o bsys.o bget_msg.o \ +@@ -42,7 +42,7 @@ + res.o rwlock.o scan.o serial.o sha1.o \ + signal.o smartall.o rblist.o tls.o tree.o \ + util.o var.o watchdog.o workq.o btimers.o \ +- address_conf.o pythonlib.o breg.o ++ address_conf.o pythonlib.o breg.o htable.o + + + EXTRAOBJS = @OBJLIST@ +Index: src/findlib/find.c +=================================================================== +--- src/findlib/find.c (révision 6467) ++++ src/findlib/find.c (copie de travail) +@@ -96,6 +96,13 @@ + Dmsg0(100, "Leave set_find_options()\n"); + } + ++void ++set_find_changed_function(FF_PKT *ff, bool check_fct(JCR *jcr, FF_PKT *ff)) ++{ ++ Dmsg0(100, "Enter set_find_changed_function()\n"); ++ ff->check_fct = check_fct; ++} ++ + /* + * For VSS we need to know which windows drives + * are used, because we create a snapshot of all used +Index: src/findlib/find_one.c +=================================================================== +--- src/findlib/find_one.c (révision 6467) ++++ src/findlib/find_one.c (copie de travail) +@@ -258,6 +258,33 @@ + } + + /* ++ * In incremental/diffential or accurate backup, we ++ * say if the current file has changed. ++ */ ++static bool check_changes(JCR *jcr, FF_PKT *ff_pkt) ++{ ++ /* in special mode (like accurate backup), user can ++ * choose his comparison function. ++ */ ++ if (ff_pkt->check_fct) { ++ return ff_pkt->check_fct(jcr, ff_pkt); ++ } ++ ++ /* in normal modes (incr/diff), we use this default ++ * behaviour ++ */ ++ if (ff_pkt->incremental && ++ (ff_pkt->statp.st_mtime < ff_pkt->save_time && ++ ((ff_pkt->flags & FO_MTIMEONLY) || ++ ff_pkt->statp.st_ctime < ff_pkt->save_time))) ++ { ++ return true; ++ } else { ++ return false; ++ } ++} ++ ++/* + * Find a single file. + * handle_file is the callback for handling the file. + * p is the filename +@@ -333,16 +360,13 @@ + * since our last "save_time", presumably the last Full save + * or Incremental. + */ +- if (ff_pkt->incremental && !S_ISDIR(ff_pkt->statp.st_mode)) { ++ if ( ff_pkt->incremental ++ && !S_ISDIR(ff_pkt->statp.st_mode) ++ && !check_changes(jcr, ff_pkt)) ++ { + Dmsg1(300, "Non-directory incremental: %s\n", ff_pkt->fname); +- /* Not a directory */ +- if (ff_pkt->statp.st_mtime < ff_pkt->save_time +- && ((ff_pkt->flags & FO_MTIMEONLY) || +- ff_pkt->statp.st_ctime < ff_pkt->save_time)) { +- /* Incremental option, file not changed */ +- ff_pkt->type = FT_NOCHG; +- return handle_file(jcr, ff_pkt, top_level); +- } ++ ff_pkt->type = FT_NOCHG; ++ return handle_file(jcr, ff_pkt, top_level); + } + + #ifdef HAVE_DARWIN_OS +@@ -502,15 +526,13 @@ + link[len] = 0; + + ff_pkt->link = link; +- if (ff_pkt->incremental && +- (ff_pkt->statp.st_mtime < ff_pkt->save_time && +- ((ff_pkt->flags & FO_MTIMEONLY) || +- ff_pkt->statp.st_ctime < ff_pkt->save_time))) { ++ if (ff_pkt->incremental && !check_changes(jcr, ff_pkt)) { + /* Incremental option, directory entry not changed */ + ff_pkt->type = FT_DIRNOCHG; + } else { + ff_pkt->type = FT_DIRBEGIN; + } ++ + /* We have set st_rdev to 1 if it is a reparse point, otherwise 0 */ + if (have_win32_api() && ff_pkt->statp.st_rdev) { + ff_pkt->type = FT_REPARSE; Index: src/findlib/find.h =================================================================== ---- src/findlib/find.h (révision 6443) +--- src/findlib/find.h (révision 6467) +++ src/findlib/find.h (copie de travail) -@@ -108,6 +108,7 @@ - #define FO_ENHANCEDWILD (1<<23) /* Enhanced wild card processing */ - #define FO_CHKCHANGES (1<<24) /* Check if file have been modified during backup */ - #define FO_STRIPPATH (1<<25) /* Check for stripping path */ -+#define FO_ACCURATE (1<<26) /* Accurate mode */ - - struct s_included_file { - struct s_included_file *next; +@@ -215,6 +215,7 @@ + findFILESET *fileset; + int (*file_save)(JCR *, FF_PKT *, bool); /* User's callback */ + int (*plugin_save)(JCR *, FF_PKT *, bool); /* User's callback */ ++ bool (*check_fct)(JCR *, FF_PKT *); /* optionnal user fct to check file changes */ + + /* Values set by accept_file while processing Options */ + uint32_t flags; /* backup options */ +Index: src/findlib/protos.h +=================================================================== +--- src/findlib/protos.h (révision 6467) ++++ src/findlib/protos.h (copie de travail) +@@ -45,6 +45,7 @@ + /* From find.c */ + FF_PKT *init_find_files(); + void set_find_options(FF_PKT *ff, int incremental, time_t mtime); ++void set_find_changed_function(FF_PKT *ff, bool check_fct(JCR *jcr, FF_PKT *ff)); + int find_files(JCR *jcr, FF_PKT *ff, int file_sub(JCR *, FF_PKT *ff_pkt, bool), + int plugin_sub(JCR *, FF_PKT *ff_pkt, bool)); + int match_files(JCR *jcr, FF_PKT *ff, int sub(JCR *, FF_PKT *ff_pkt, bool));