+++ /dev/null
- Add a new runswhen directive ClientRunAfterVSS
- "Run After VSS snapshot, but before backup"
-
- It can be applied to Bacula trunk (and probably earlier
- 2.2.x versions) with:
-
- cd <bacula-source>
- ./configure <your options>
- patch -p0 <clientrunaftervss.patch
- make
- ...
- make install
-
-Index: src/dird/dird_conf.c
-===================================================================
---- src/dird/dird_conf.c (révision 6325)
-+++ src/dird/dird_conf.c (copie de travail)
-@@ -1688,10 +1688,12 @@
- *(int *)(item->value) = SCRIPT_Before ;
- } else if (strcasecmp(lc->str, "after") == 0) {
- *(int *)(item->value) = SCRIPT_After;
-+ } else if (strcasecmp(lc->str, "aftervss") == 0) {
-+ *(int *)(item->value) = SCRIPT_AfterVSS;
- } else if (strcasecmp(lc->str, "always") == 0) {
- *(int *)(item->value) = SCRIPT_Any;
- } else {
-- scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
-+ scan_err2(lc, _("Expect %s, got: %s"), "Before, After, AfterVSS or Always", lc->str);
- }
- scan_to_eol(lc);
- }
-Index: src/filed/job.c
-===================================================================
---- src/filed/job.c (révision 6325)
-+++ src/filed/job.c (copie de travail)
-@@ -1445,6 +1445,7 @@
- berrno be;
- Jmsg(jcr, M_WARNING, 0, _("VSS was not initialized properly. VSS support is disabled. ERR=%s\n"), be.bstrerror());
- }
-+ run_scripts(jcr, jcr->RunScripts, "ClientAfterVSS");
- }
- #endif
-
-Index: src/lib/runscript.h
-===================================================================
---- src/lib/runscript.h (révision 6325)
-+++ src/lib/runscript.h (copie de travail)
-@@ -58,6 +58,7 @@
- SCRIPT_Never = 0,
- SCRIPT_After = (1<<0), /* AfterJob */
- SCRIPT_Before = (1<<1), /* BeforeJob */
-+ SCRIPT_AfterVSS = (1<<2), /* BeforeJob and After VSS */
- SCRIPT_Any = SCRIPT_Before | SCRIPT_After
- };
-
-Index: src/lib/runscript.c
-===================================================================
---- src/lib/runscript.c (révision 6325)
-+++ src/lib/runscript.c (copie de travail)
-@@ -116,6 +116,8 @@
-
- if (strstr(label, NT_("Before"))) {
- when = SCRIPT_Before;
-+ } else if (bstrcmp(label, NT_("ClientAfterVSS"))) {
-+ when = SCRIPT_AfterVSS;
- } else {
- when = SCRIPT_After;
- }
-@@ -142,6 +144,18 @@
- }
- }
-
-+ if ((script->when & SCRIPT_AfterVSS) && (when & SCRIPT_AfterVSS)) {
-+ if ((script->on_success && (jcr->JobStatus == JS_Blocked))
-+ || (script->on_failure && job_canceled(jcr))
-+ )
-+ {
-+ Dmsg4(200, "runscript: Run it because SCRIPT_AfterVSS (%s,%i,%i,%c)\n",
-+ script->command, script->on_success, script->on_failure,
-+ jcr->JobStatus );
-+ runit = true;
-+ }
-+ }
-+
- if ((script->when & SCRIPT_After) && (when & SCRIPT_After)) {
- if ((script->on_success && (jcr->JobStatus == JS_Terminated))
- || (script->on_failure && job_canceled(jcr))
+++ /dev/null
-Index: src/filed/backup.c
-===================================================================
---- src/filed/backup.c (revision 6497)
-+++ src/filed/backup.c (working copy)
-@@ -53,7 +53,8 @@
- typedef struct CurFile {
- hlink link;
- char *fname;
-- char *lstat;
-+ time_t ctime;
-+ time_t mtime;
- bool seen;
- } CurFile;
-
-@@ -66,24 +67,12 @@
- * the last time. After we can compare Lstat field.
- *
- */
--/* TODO: tweak verify code to use the same function ?? */
- bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
- {
-- char *p;
- int stat=false;
-- struct stat statc; /* catalog stat */
-- char *Opts_Digest;
- char *fname;
- CurFile *elt;
-
-- int32_t LinkFIc;
--
-- if (*ff_pkt->VerifyOpts) { /* use mtime + ctime checks by default */
-- Opts_Digest = ff_pkt->VerifyOpts;
-- } else {
-- Opts_Digest = "cm";
-- }
--
- if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
- return true;
- }
-@@ -110,95 +99,14 @@
- goto bail_out;
- }
-
-- decode_stat(elt->lstat, &statc, &LinkFIc); /* decode catalog stat */
--// *do_Digest = CRYPTO_DIGEST_NONE;
--
-- for (p=Opts_Digest; *p; p++) {
-- char ed1[30], ed2[30];
-- switch (*p) {
-- case 'i': /* compare INODEs */
-- 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_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_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_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_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_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_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_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;
-- case '1': /* compare SHA1 */
--// *do_Digest = CRYPTO_DIGEST_SHA1;
-- break;
-- case ':':
-- case 'V':
-- default:
-- break;
-- }
-+ if (elt->mtime != ff_pkt->statp.st_mtime) {
-+ Jmsg(jcr, M_SAVED, 0, _("%s st_mtime differs\n"), fname);
-+ stat = true;
-+ } else if (elt->ctime != ff_pkt->statp.st_ctime) {
-+ Jmsg(jcr, M_SAVED, 0, _("%s st_ctime differs\n"), fname);
-+ stat = true;
- }
-+
- accurate_mark_file_as_seen(elt);
- Dmsg2(500, "accurate %s = %i\n", fname, stat);
-
-@@ -215,8 +123,11 @@
- {
- BSOCK *dir = jcr->dir_bsock;
- int len;
-+ struct stat statp;
-+ int32_t LinkFIc;
- uint64_t nb;
- CurFile *elt=NULL;
-+ char *lstat;
-
- if (jcr->accurate==false || job_canceled(jcr) || jcr->JobLevel==L_FULL) {
- return true;
-@@ -246,10 +157,13 @@
- elt->fname = (char *) elt+sizeof(CurFile);
- memcpy(elt->fname, dir->msg, dir->msglen);
- elt->fname[dir->msglen]='\0';
-- elt->lstat = elt->fname + len + 1;
-- elt->seen=0;
-+ lstat = elt->fname + len + 1;
-+ decode_stat(lstat, &statp, &LinkFIc); /* decode catalog stat */
-+ elt->ctime = statp.st_ctime;
-+ elt->mtime = statp.st_mtime;
-+ elt->seen = 0;
- jcr->file_list->insert(elt->fname, elt);
-- Dmsg2(500, "add fname=%s lstat=%s\n", elt->fname, elt->lstat);
-+ Dmsg2(500, "add fname=%s lstat=%s\n", elt->fname, lstat);
- }
- }
-
-@@ -280,9 +194,10 @@
-
- foreach_htable (elt, jcr->file_list) {
- if (!accurate_file_has_been_seen(elt)) { /* already seen */
-- Dmsg3(500, "deleted fname=%s lstat=%s seen=%i\n", elt->fname, elt->lstat, elt->seen);
-+ Dmsg2(500, "deleted fname=%s seen=%i\n", elt->fname, elt->seen);
- ff_pkt->fname = elt->fname;
-- decode_stat(elt->lstat, &ff_pkt->statp, &ff_pkt->LinkFI); /* decode catalog stat */
-+ ff_pkt->statp.st_mtime = elt->mtime;
-+ ff_pkt->statp.st_ctime = elt->ctime;
- encode_and_send_attributes(jcr, ff_pkt, stream);
- }
- // Free(elt->fname);
+++ /dev/null
-Index: src/dird/fd_cmds.c
-===================================================================
---- src/dird/fd_cmds.c (révision 6372)
-+++ src/dird/fd_cmds.c (copie de travail)
-@@ -50,7 +50,7 @@
- static char filesetcmd[] = "fileset%s\n"; /* set full fileset */
- static char jobcmd[] = "JobId=%s Job=%s SDid=%u SDtime=%u Authorization=%s\n";
- /* Note, mtime_only is not used here -- implemented as file option */
--static char levelcmd[] = "level = %s%s mtime_only=%d\n";
-+static char levelcmd[] = "level = %s%s%s mtime_only=%d\n";
- static char runscript[] = "Run OnSuccess=%u OnFailure=%u AbortOnError=%u When=%u Command=%s\n";
- static char runbeforenow[]= "RunBeforeNow\n";
-
-@@ -189,6 +189,12 @@
- bsnprintf(since, since_len, _(" (upgraded from %s)"),
- level_to_str(jcr->JobLevel));
- jcr->JobLevel = jcr->jr.JobLevel = L_FULL;
-+ /* look if we found the last accurate backup */
-+ } else if (jcr->accurate && !db_accurate_find_backupid(jcr, jcr->db, &jcr->jr)) {
-+ Jmsg(jcr, M_INFO, 0, _("No prior or suitable Full accurate backup found in catalog. Doing FULL backup.\n"));
-+ bsnprintf(since, since_len, _(" (upgraded from %s)"),
-+ level_to_str(jcr->JobLevel));
-+ jcr->JobLevel = jcr->jr.JobLevel = L_FULL;
- } else {
- if (jcr->job->rerun_failed_levels) {
- if (db_find_failed_job_since(jcr, jcr->db, &jcr->jr, jcr->stime, JobLevel)) {
-@@ -217,7 +223,7 @@
- char ed1[50];
-
- stime = str_to_utime(jcr->stime);
-- fd->fsend(levelcmd, NT_("since_utime "), edit_uint64(stime, ed1), 0);
-+ fd->fsend(levelcmd, "", NT_("since_utime "), edit_uint64(stime, ed1), 0);
- while (bget_dirmsg(fd) >= 0) { /* allow him to poll us to sync clocks */
- Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg);
- }
-@@ -231,24 +237,25 @@
- bool send_level_command(JCR *jcr)
- {
- BSOCK *fd = jcr->file_bsock;
-+ const char *accurate=jcr->job->accurate?"accurate_":"";
- /*
- * Send Level command to File daemon
- */
- switch (jcr->JobLevel) {
- case L_BASE:
-- fd->fsend(levelcmd, "base", " ", 0);
-+ fd->fsend(levelcmd, "", "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);
- break;
- case L_DIFFERENTIAL:
-- fd->fsend(levelcmd, "differential", " ", 0);
-+ fd->fsend(levelcmd, accurate, "differential", " ", 0);
- send_since_time(jcr);
- break;
- case L_INCREMENTAL:
-- fd->fsend(levelcmd, "incremental", " ", 0);
-+ fd->fsend(levelcmd, accurate, "incremental", " ", 0);
- send_since_time(jcr);
- break;
- case L_SINCE:
-Index: src/dird/backup.c
-===================================================================
---- src/dird/backup.c (révision 6372)
-+++ 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,450 @@
- return true;
- }
-
-+static int accurate_list_handler(void *ctx, int num_fields, char **row)
-+{
-+ JCR *jcr = (JCR *)ctx;
-+
-+ if (job_canceled(jcr)) {
-+ return 1;
-+ }
-+
-+ if (row[0] > 0) {
-+ jcr->file_bsock->fsend("%s%s%c%s", row[1], row[2], 0, row[3]);
-+ }
-+ return 0;
-+}
-+
-+bool db_get_jobids(JCR *jcr)
-+{
-+
-+}
-+
-+bool accurate_send_current_files(JCR *jcr)
-+{
-+ char buf[MAXSTRING];
-+ char ed1[50], ed2[50];
- /*
-+ 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
-+*/
-+ bsnprintf(buf, sizeof(buf),
-+ "SELECT DISTINCT ON (PathId, FilenameId) FileIndex, Path, Name, LStat "
-+ "FROM File JOIN Filename USING (FilenameId) JOIN Path USING (PathId) WHERE JobId IN (%s)"
-+ "ORDER BY PathId, FilenameId, JobId DESC",
-+ "10,20,30"); /* jobid */
-+
-+ Dmsg1(2, "display current files cmd=%s\n", buf);
-+ db_sql_query(jcr->db, buf, accurate_list_handler, (void *)jcr);
-+ jcr->file_bsock->signal(BNET_EOD);
-+ return true;
-+}
-+
-+/*
-+ * We are called here for each record that matches the above
-+ * SQL query -- that is for each file contained in the Catalog
-+ * that was not marked earlier. This means that the file in
-+ * question is a missing file (in the Catalog but not on Disk).
-+ */
-+/* TODO: tweak verify code to use the same function */
-+bool accurate_check_file(JCR *jcr, FILE_DBR *fdbr, char *attr, char *Opts_Digest, int *do_Digest)
-+{
-+ char *p;
-+ int stat=false;
-+ struct stat statf; /* file stat */
-+ struct stat statc; /* catalog stat */
-+
-+ int32_t LinkFIf, LinkFIc;
-+
-+ decode_stat(attr, &statf, &LinkFIf); /* decode file stat packet */
-+ decode_stat(fdbr->LStat, &statc, &LinkFIc); /* decode catalog stat */
-+ *do_Digest = CRYPTO_DIGEST_NONE;
-+
-+ for (p=Opts_Digest; *p; p++) {
-+ char ed1[30], ed2[30];
-+ switch (*p) {
-+ case 'i': /* compare INODEs */
-+ if (statc.st_ino != statf.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)statf.st_ino, ed2));
-+ stat = true;
-+ }
-+ break;
-+ case 'p': /* permissions bits */
-+ if (statc.st_mode != statf.st_mode) {
-+ Jmsg(jcr, M_INFO, 0, _(" st_mode differ. Cat: %x File: %x\n"),
-+ (uint32_t)statc.st_mode, (uint32_t)statf.st_mode);
-+ stat = true;
-+ }
-+ break;
-+ case 'n': /* number of links */
-+ if (statc.st_nlink != statf.st_nlink) {
-+ Jmsg(jcr, M_INFO, 0, _(" st_nlink differ. Cat: %d File: %d\n"),
-+ (uint32_t)statc.st_nlink, (uint32_t)statf.st_nlink);
-+ stat = true;
-+ }
-+ break;
-+ case 'u': /* user id */
-+ if (statc.st_uid != statf.st_uid) {
-+ Jmsg(jcr, M_INFO, 0, _(" st_uid differ. Cat: %u File: %u\n"),
-+ (uint32_t)statc.st_uid, (uint32_t)statf.st_uid);
-+ stat = true;
-+ }
-+ break;
-+ case 'g': /* group id */
-+ if (statc.st_gid != statf.st_gid) {
-+ Jmsg(jcr, M_INFO, 0, _(" st_gid differ. Cat: %u File: %u\n"),
-+ (uint32_t)statc.st_gid, (uint32_t)statf.st_gid);
-+ stat = true;
-+ }
-+ break;
-+ case 's': /* size */
-+ if (statc.st_size != statf.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)statf.st_size, ed2));
-+ stat = true;
-+ }
-+ break;
-+ case 'a': /* access time */
-+ if (statc.st_atime != statf.st_atime) {
-+ Jmsg(jcr, M_INFO, 0, _(" st_atime differs\n"));
-+ stat = true;
-+ }
-+ break;
-+ case 'm':
-+ if (statc.st_mtime != statf.st_mtime) {
-+ Jmsg(jcr, M_INFO, 0, _(" st_mtime differs\n"));
-+ stat = true;
-+ }
-+ break;
-+ case 'c': /* ctime */
-+ if (statc.st_ctime != statf.st_ctime) {
-+ Jmsg(jcr, M_INFO, 0, _(" st_ctime differs\n"));
-+ stat = true;
-+ }
-+ break;
-+ case 'd': /* file size decrease */
-+ if (statc.st_size > statf.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)statf.st_size, ed2));
-+ stat = true;
-+ }
-+ break;
-+ case '5': /* compare MD5 */
-+ Dmsg1(500, "set Do_MD5 for %s\n", jcr->fname);
-+ *do_Digest = CRYPTO_DIGEST_MD5;
-+ break;
-+ case '1': /* compare SHA1 */
-+ *do_Digest = CRYPTO_DIGEST_SHA1;
-+ break;
-+ case ':':
-+ case 'V':
-+ default:
-+ break;
-+ }
-+ }
-+ return stat;
-+}
-+
-+/*
-+ * This function is called at EOJ.
-+ * For a Full backup, we remove old one, and we add all entries
-+ * For an Incremental, we add all entries (delete have been before)
-+ * For a Differential, we add all entries (delete have been before)
-+ *
-+ * TODO:
-+ *
-+ */
-+bool accurate_update_current_files(JCR *jcr)
-+{
-+ JobId_t backupid;
-+
-+ if (jcr->accurate == false) {
-+ return true;
-+ }
-+
-+ backupid = db_accurate_find_backupid(jcr, jcr->db, &jcr->jr);
-+
-+ Dmsg1(1, "backupid = %i\n", backupid);
-+
-+ if (!backupid) {
-+ return false; /* something goes wrong */
-+ }
-+
-+ if (jcr->JobLevel == L_FULL) {
-+ db_accurate_cleanup_currentfile(jcr, jcr->db, backupid);
-+ }
-+
-+ db_accurate_update_currentfile(jcr, jcr->db, jcr->JobId,
-+ jcr->JobLevel, backupid);
-+ return true;
-+}
-+
-+/*
-+ * We are called here for each record that matches the above
-+ * SQL query -- that is for each file contained in the Catalog
-+ * that was not marked earlier. This means that the file in
-+ * question is a missing file (in the Catalog but not on Disk).
-+ */
-+static int accurate_handler(void *ctx, int num_fields, char **row)
-+{
-+ JCR *jcr = (JCR *)ctx;
-+
-+ if (job_canceled(jcr)) {
-+ return 1;
-+ }
-+ if (num_fields == 2) { /* deleted files */
-+ jcr->file_bsock->fsend("%s%s", row[0]?row[0]:"", row[1]?row[1]:"");
-+ } else if (num_fields == 1) { /* files to backup */
-+ jcr->file_bsock->fsend("%s", row[0]?row[0]:"");
-+ }
-+ return 0;
-+}
-+
-+/*
-+ * Send deleted files and files to backup in accurate mode
-+ *
-+ */
-+static int accurate_send_missing_and_deleted_files(JCR *jcr, JobId_t BackupId)
-+{
-+ char buf[MAXSTRING];
-+ char ed1[50], ed2[50];
-+
-+ bsnprintf(buf, sizeof(buf),
-+ "SELECT Name FROM ToBackup%s",
-+ edit_uint64(jcr->JobId, ed2));
-+
-+ /* missing_handler is called for each file found */
-+ Dmsg1(2, "display files to backup cmd=%s\n", buf);
-+ db_sql_query(jcr->db, buf, accurate_handler, (void *)jcr);
-+ jcr->file_bsock->signal(BNET_EOD);
-+
-+ bsnprintf(buf, sizeof(buf),
-+ "SELECT Path.Path,Filename.Name "
-+ "FROM CurrentFile "
-+ "JOIN File USING (FileId) "
-+ "JOIN Path USING (PathId) "
-+ "JOIN Filename USING (FilenameId) "
-+ "WHERE CurrentFile.BackupId=%s "
-+ "AND CurrentFile.MarkId!=%s ",
-+ edit_uint64(BackupId, ed1), edit_uint64(jcr->JobId, ed2));
-+ /* missing_handler is called for each file found */
-+ Dmsg1(2, "display deleted files cmd=%s\n", buf);
-+ db_sql_query(jcr->db, buf, accurate_handler, (void *)jcr);
-+ jcr->file_bsock->signal(BNET_EOD);
-+
-+ return 1;
-+}
-+
-+/*
-+ * Accurate backup mode
-+ * 1. Receive the list of all files including those backed up to the Dir
-+ * 2. Dir computes files and deleted files.
-+ * 3. Dir sends list of additional files (new files) to backup,
-+ * and list of files deleted.
-+ *
-+ * Cleanup attributes (don't use atime, inode etc..)
-+ * Need to insert file and attributes to temp table ?
-+ * Batch compare files and attributes ?
-+ *
-+ * If file have file_index=0, they are discarded by FD
-+ *
-+ * TODO: send deleted list and new list to client
-+ * tweak SD with file_index=-1
-+ */
-+bool accurate_compute_files(JCR *jcr)
-+{
-+ BSOCK *fd;
-+ char buf[MAXSTRING];
-+ int n, len;
-+ FILE_DBR fdbr;
-+ POOLMEM *fname = get_pool_memory(PM_MESSAGE);
-+ int do_Digest = CRYPTO_DIGEST_NONE;
-+ int32_t file_index = 0;
-+ JobId_t JobId=0; /* TODO: compute the job key in new table */
-+ JobId_t backupid=0;
-+
-+ memset(&fdbr, 0, sizeof(FILE_DBR));
-+ fd = jcr->file_bsock;
-+ fdbr.JobId = JobId;
-+ jcr->FileIndex = 0;
-+
-+ if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
-+ return true;
-+ }
-+
-+ backupid = db_accurate_find_backupid(jcr, jcr->db, &jcr->jr);
-+ if (!backupid) {
-+ Jmsg(jcr, M_FATAL, 0, _("Can't use Accurate mode ERR=Can't find BackupId\n"));
-+ return false;
-+ }
-+ db_accurate_create_tobackup_table(jcr, jcr->db, jcr->JobId);
-+ Dmsg0(1, "bdird: waiting to receive file attributes\n");
-+ /*
-+ * Get Attributes and Signature from File daemon
-+ * We expect:
-+ * FileIndex
-+ * Stream
-+ * Options or Digest (MD5/SHA1)
-+ * Filename
-+ * Attributes
-+ * Link name ???
-+ */
-+ while ((n=bget_dirmsg(fd)) >= 0 && !job_canceled(jcr)) {
-+ int stream;
-+ char *attr, *p, *fn;
-+ char Opts_Digest[MAXSTRING]; /* Verify Opts or MD5/SHA1 digest */
-+
-+ if (job_canceled(jcr)) {
-+ goto bail_out2;
-+ }
-+ fname = check_pool_memory_size(fname, fd->msglen);
-+ jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
-+ Dmsg1(1, "Atts+Digest=%s\n", fd->msg);
-+ if ((len = sscanf(fd->msg, "%ld %d %100s", &file_index, &stream,
-+ fname)) != 3) {
-+ Jmsg3(jcr, M_FATAL, 0, _("bird<filed: bad attributes, expected 3 fields got %d\n"
-+" mslen=%d msg=%s\n"), len, fd->msglen, fd->msg);
-+ goto bail_out2;
-+ }
-+ /*
-+ * We read the Options or Signature into fname
-+ * to prevent overrun, now copy it to proper location.
-+ */
-+ bstrncpy(Opts_Digest, fname, sizeof(Opts_Digest));
-+ p = fd->msg;
-+ skip_nonspaces(&p); /* skip FileIndex */
-+ skip_spaces(&p);
-+ skip_nonspaces(&p); /* skip Stream */
-+ skip_spaces(&p);
-+ skip_nonspaces(&p); /* skip Opts_Digest */
-+ p++; /* skip space */
-+ fn = fname;
-+ while (*p != 0) {
-+ *fn++ = *p++; /* copy filename */
-+ }
-+ *fn = *p++; /* term filename and point to attribs */
-+ attr = p;
-+ /*
-+ * Got attributes stream, decode it
-+ */
-+ if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
-+ int changed=true;
-+ Dmsg2(400, "file_index=%d attr=%s\n", file_index, attr);
-+ jcr->JobFiles++;
-+ jcr->FileIndex = file_index; /* remember attribute file_index */
-+ do_Digest = CRYPTO_DIGEST_NONE;
-+ pm_strcpy(jcr->fname, fname); /* move filename into JCR */
-+ fdbr.FileId = 0;
-+
-+ /*
-+ * Find equivalent record in the database
-+ */
-+
-+ if (db_accurate_get_file_attributes_record(jcr, jcr->db, jcr->fname,
-+ backupid, &fdbr))
-+ {
-+ Dmsg2(1, "get_file ok fname=%s fileid=%i\n", jcr->fname, fdbr.FileId);
-+ if (fdbr.MarkId != jcr->JobId) { /* Already visited ? */
-+ if (file_index == 0) { /* file not saved */
-+ changed = accurate_check_file(jcr, &fdbr, attr, Opts_Digest, &do_Digest);
-+ Dmsg1(1, "check_file changed=%i\n", changed);
-+
-+ if (changed == true) {
-+ db_accurate_mark_file_for_backup(jcr, jcr->db, jcr->fname, jcr->JobId);
-+ db_accurate_delete_file_record(jcr, jcr->db, fdbr.FileId, backupid);
-+ } else {
-+ db_accurate_mark_file_record(jcr, jcr->db, backupid,
-+ fdbr.FileId, jcr->JobId);
-+ }
-+ } else { /* file_index != 0 file have be backuped */
-+ db_accurate_delete_file_record(jcr, jcr->db, fdbr.FileId, backupid);
-+ }
-+ } else {
-+ Dmsg2(1, "already saved fname=%s fileid=%i\n", jcr->fname, fdbr.FileId);
-+ }
-+ } else if (file_index == 0) {
-+ Dmsg1(1, "mark_for_backup fname=%s\n", jcr->fname);
-+ db_accurate_mark_file_for_backup(jcr, jcr->db, jcr->fname, jcr->JobId);
-+ }
-+
-+ /*
-+ * Got Digest Signature from Storage daemon
-+ * It came across in the Opts_Digest field.
-+ */
-+ /* not used */
-+ } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
-+ Dmsg2(400, "stream=Digest inx=%d Digest=%s\n", file_index, Opts_Digest);
-+ /*
-+ * When ever we get a digest it MUST have been
-+ * preceded by an attributes record, which sets attr_file_index
-+ */
-+ if (jcr->FileIndex != (uint32_t)file_index) {
-+ Jmsg2(jcr, M_FATAL, 0, _("MD5/SHA1 index %d not same as attributes %d\n"),
-+ file_index, jcr->FileIndex);
-+ goto bail_out2;
-+ }
-+ if (do_Digest != CRYPTO_DIGEST_NONE) {
-+ db_escape_string(jcr, jcr->db, buf, Opts_Digest, strlen(Opts_Digest));
-+ if (strcmp(buf, fdbr.Digest) != 0) {
-+ if (debug_level >= 10) {
-+ Jmsg(jcr, M_INFO, 0, _(" %d not same. File=%s Cat=%s\n"),
-+ stream, buf, fdbr.Digest);
-+ } else {
-+ Jmsg(jcr, M_INFO, 0, _(" %d differs.\n"),
-+ stream);
-+ }
-+ //stat = JS_Differences;
-+ }
-+ do_Digest = CRYPTO_DIGEST_NONE;
-+ }
-+ }
-+// jcr->JobFiles = file_index;
-+ }
-+ if (is_bnet_error(fd)) {
-+ berrno be;
-+ Jmsg2(jcr, M_FATAL, 0, _("bdird<filed: bad attributes from filed n=%d : %s\n"),
-+ n, be.bstrerror());
-+ goto bail_out2;
-+ }
-+
-+/*
-+CREATE VIEW cf AS SELECT path.path || filename.name as filename,
-+ jobid, currentfile.markid, backupid
-+ FROM File join currentfile using (fileid) join filename using (filenameid) join path using (pathid)
-+*/
-+
-+ accurate_send_missing_and_deleted_files(jcr, backupid);
-+
-+ db_accurate_clean_deleted_files(jcr, jcr->db, jcr->JobId, backupid);
-+
-+ db_accurate_drop_tobackup_table(jcr, jcr->db, jcr->JobId);
-+
-+ free_pool_memory(fname);
-+ return true;
-+
-+bail_out2:
-+ db_accurate_drop_tobackup_table(jcr, jcr->db, jcr->JobId);
-+ return false;
-+}
-+
-+/*
- * Do a backup of the specified FileSet
- *
- * Returns: false on failure
-@@ -231,9 +675,18 @@
- goto bail_out;
- }
-
-+ /*
-+ * If backup is in accurate mode, FD will send the list of
-+ * all files. We have to store it, and compute witch files
-+ * have been deleted and witch files have to be backuped.
-+ */
-+ accurate_compute_files(jcr);
-+
- /* Pickup Job termination data */
- stat = wait_for_job_termination(jcr);
- db_write_batch_file_records(jcr); /* used by bulk batch file insert */
-+ accurate_update_current_files(jcr);
-+
- if (stat == JS_Terminated) {
- backup_cleanup(jcr, stat);
- return true;
-Index: src/dird/inc_conf.c
-===================================================================
---- src/dird/inc_conf.c (révision 6372)
-+++ 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
- };
-
- /*
-@@ -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}
- };
-
-Index: src/filed/backup.c
-===================================================================
---- src/filed/backup.c (révision 6372)
-+++ src/filed/backup.c (copie de travail)
-@@ -48,8 +48,114 @@
- 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);
-
- /*
-+ * Called by save_file when accept/discard file for backup
-+ *
-+ * TODO: we could add MD5/SHAX digest, but we have to compute it
-+ * for all files.
-+ */
-+static bool accurate_add_file(JCR *jcr, FF_PKT *ff_pkt, char *stats)
-+{
-+ char *a=stats;
-+ char attribs[MAXSTRING];
-+ uint32_t file_index=jcr->JobFiles;
-+ BSOCK *dir = jcr->dir_bsock;
-+ int stat;
-+
-+ if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
-+ return true;
-+ }
-+
-+ if (!stats) { /* TODO: don't always compute attribute */
-+ file_index=0;
-+ encode_stat(attribs, ff_pkt, 0);
-+ a = attribs;
-+ }
-+
-+ switch (ff_pkt->type) {
-+ case FT_LNKSAVED: /* Hard linked, file already saved */
-+ case FT_LNK:
-+ stat = dir->fsend("%d %d %s %s%c%s%c%s%c", file_index,
-+ STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname,
-+ 0, a, 0, ff_pkt->link, 0);
-+ break;
-+ case FT_REGE:
-+ case FT_REG:
-+ case FT_SPEC:
-+ case FT_RAW:
-+ case FT_FIFO:
-+ case FT_NOCHG:
-+ stat = dir->fsend("%d %d %s %s%c%s%c%c", file_index,
-+ STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname,
-+ 0, a, 0, 0);
-+
-+ break;
-+ case FT_REPARSE:
-+ case FT_DIREND:
-+ case FT_NORECURSE:
-+ case FT_DIRNOCHG:
-+ stat = dir->fsend("%d %d %s %s%c%s%c%c", file_index,
-+ STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->link,
-+ 0, a, 0, 0);
-+ break;
-+ default:
-+ Dmsg2(1, _("Fname=%s Type=%i\n"), ff_pkt->fname, ff_pkt->type);
-+ return true;
-+ }
-+
-+ if (!stat) {
-+ Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), bnet_strerror(dir));
-+ return 0;
-+ }
-+
-+ return true;
-+}
-+
-+/* build a fileset with new files from director */
-+static bool accurate_get_new_and_deleted_file_list(JCR *jcr)
-+{
-+ BSOCK *dir = jcr->dir_bsock;
-+ if (jcr->accurate == false || job_canceled(jcr)) {
-+ return true;
-+ }
-+
-+ /* get missing files */
-+ while (dir->recv() >= 0) {
-+ Dmsg1(1, "missing = %s\n", dir->msg);
-+ }
-+
-+ /* get deleted files */
-+ while (dir->recv() >= 0) {
-+ Dmsg1(1, "deleted = %s\n", dir->msg);
-+ encode_and_send_deleted_file(jcr, dir->msg);
-+ }
-+
-+ return true;
-+}
-+
-+/* send deleted file list to stored */
-+static bool accurate_send_deleted_list(JCR *jcr)
-+{
-+ if (jcr->accurate == false || job_canceled(jcr)) {
-+ return true;
-+ }
-+ return true;
-+}
-+
-+static bool accurate_send_file_list(JCR *jcr)
-+{
-+ if (jcr->accurate == false || job_canceled(jcr)) {
-+ return true;
-+ }
-+ Dmsg0(1, "Sending BNET_EOD\n");
-+ jcr->dir_bsock->signal(BNET_EOD); /* end of sending data */
-+ return true;
-+}
-+
-+
-+/*
- * Find all the requested files and send them
- * to the Storage daemon.
- *
-@@ -66,7 +172,6 @@
- BSOCK *sd;
- bool ok = true;
- // TODO landonf: Allow user to specify encryption algorithm
--
- sd = jcr->store_bsock;
-
- set_jcr_job_status(jcr, JS_Running);
-@@ -134,6 +239,20 @@
- ok = false; /* error */
- set_jcr_job_status(jcr, JS_ErrorTerminated);
- }
-+ Dmsg1(1, "jcr->accurate == %i\n", jcr->accurate);
-+ /* start accurate stuffs */
-+ if (jcr->accurate) {
-+ /* TODO: test job_canceled() */
-+ accurate_send_file_list(jcr); /* send all files to DIR */
-+ accurate_get_new_and_deleted_file_list(jcr); /* get a new incr fileset from DIR */
-+// set_find_options((FF_PKT *)jcr->ff, 0, 0); /* we backup all that director wants */
-+// if (!find_files(jcr, (FF_PKT *)jcr->ff, save_file)) {
-+// ok = false; /* error */
-+// set_jcr_job_status(jcr, JS_ErrorTerminated);
-+// }
-+// accurate_send_file_list(jcr); /* send all new files to DIR */
-+ accurate_send_deleted_list(jcr); /* send deleted list to SD */
-+ }
-
- free_pool_memory(jcr->acl_text);
-
-@@ -355,9 +474,11 @@
- case FT_DIRNOCHG:
- case FT_NOCHG:
- Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname);
-+ accurate_add_file(jcr, ff_pkt, NULL); /* list skipped files */
- return 1;
- case FT_ISARCH:
- Jmsg(jcr, M_NOTSAVED, 0, _(" Archive file not saved: %s\n"), ff_pkt->fname);
-+ accurate_add_file(jcr, ff_pkt, NULL); /* list skipped files */
- return 1;
- case FT_NOOPEN: {
- berrno be;
-@@ -1111,6 +1232,9 @@
- }
- unstrip_path(ff_pkt);
-
-+ /* list backuped files */
-+ accurate_add_file(jcr, ff_pkt, attribs);
-+
- 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"),
-@@ -1121,6 +1245,58 @@
- return true;
- }
-
-+static bool encode_and_send_deleted_file(JCR *jcr, char *fname)
-+{
-+ BSOCK *sd = jcr->store_bsock;
-+ char *attribs;
-+ char *attribsEx;
-+ int stat;
-+#ifdef FD_NO_SEND_TEST
-+ return true;
-+#endif
-+
-+ attribs = " ";
-+ attribsEx = " ";
-+
-+ /*
-+ * Send Attributes header to Storage daemon
-+ * <file-index> <stream> <info>
-+ */
-+ if (!sd->fsend("%ld %d 0", 0, STREAM_UNIX_ATTRIBUTES)) {
-+ Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-+ sd->bstrerror());
-+ return false;
-+ }
-+ Dmsg1(300, ">stored: attrhdr %s\n", sd->msg);
-+
-+ /*
-+ * Send file attributes to Storage daemon
-+ * File_index
-+ * File type
-+ * Filename (full path)
-+ * Encoded attributes
-+ * Link name (if type==FT_LNK or FT_LNKSAVED)
-+ * Encoded extended-attributes (for Win32)
-+ *
-+ * 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);
-+
-+ 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"),
-+ sd->bstrerror());
-+ return false;
-+ }
-+ sd->signal(BNET_EOD); /* indicate end of attributes data */
-+ return true;
-+}
-+
- /*
- * Do in place strip of path
- */
-Index: src/filed/job.c
-===================================================================
---- src/filed/job.c (révision 6372)
-+++ src/filed/job.c (copie de travail)
-@@ -1087,6 +1087,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 +1198,9 @@
-
- level = get_memory(dir->msglen+1);
- Dmsg1(110, "level_cmd: %s", dir->msg);
-+ if (strstr(dir->msg, "accurate")) {
-+ jcr->accurate = true;
-+ }
- if (sscanf(dir->msg, "level = %s ", level) != 1) {
- goto bail_out;
- }
-@@ -1204,14 +1210,14 @@
- /* Full backup requested? */
- } else if (strcmp(level, "full") == 0) {
- jcr->JobLevel = L_FULL;
-- } else if (strcmp(level, "differential") == 0) {
-+ } else if (strstr(level, "differential")) {
- jcr->JobLevel = L_DIFFERENTIAL;
- free_memory(level);
- return 1;
-- } else if (strcmp(level, "incremental") == 0) {
-+ } else if (strstr(level, "incremental")) {
- jcr->JobLevel = L_INCREMENTAL;
- free_memory(level);
-- return 1;
-+ return 1;
- /*
- * We get his UTC since time, then sync the clocks and correct it
- * to agree with our clock.
-Index: src/cats/sql_update.c
-===================================================================
---- src/cats/sql_update.c (révision 6372)
-+++ src/cats/sql_update.c (copie de travail)
-@@ -88,6 +88,102 @@
- return stat;
- }
-
-+int db_accurate_delete_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t BackupId)
-+{
-+ int stat;
-+ char ed1[50], ed2[50];
-+ db_lock(mdb);
-+ Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE FileId=%s AND BackupId=%s",
-+ edit_int64(FileId, ed1), edit_int64(BackupId, ed2));
-+ stat = INSERT_DB(jcr, mdb, mdb->cmd);
-+ db_unlock(mdb);
-+ return stat;
-+}
-+
-+int db_accurate_mark_file_for_backup(JCR *jcr, B_DB *mdb, char *fname, JobId_t JobId)
-+{
-+ int stat;
-+ char ed1[50];
-+ db_lock(mdb);
-+ /* TODO: mdb->esc_xxx are already ok but it's more smart to recompute it */
-+// mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*len+2);
-+// mdb->esc_name = db_escape_string(jcr, mdb, mdb->esc_name, fname, len);
-+ Mmsg(mdb->cmd, "INSERT INTO ToBackup%s (name) VALUES ('%s%s')", edit_int64(JobId, ed1), mdb->esc_path, mdb->esc_name);
-+ stat = INSERT_DB(jcr, mdb, mdb->cmd);
-+ db_unlock(mdb);
-+ return stat;
-+}
-+
-+int db_accurate_cleanup_currentfile(JCR *jcr, B_DB *mdb, JobId_t BackupId)
-+{
-+ int stat;
-+ char ed1[50];
-+ db_lock(mdb);
-+ Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE BackupId=%s", edit_int64(BackupId, ed1));
-+ stat = QUERY_DB(jcr, mdb, mdb->cmd);
-+ db_unlock(mdb);
-+ return stat;
-+}
-+
-+int db_accurate_update_currentfile(JCR *jcr, B_DB *mdb, JobId_t JobId, int JobLevel, JobId_t BackupId)
-+{
-+ int stat;
-+ char ed1[50], ed2[50], ed3[50];
-+ db_lock(mdb);
-+ edit_int64(JobId, ed2);
-+ Mmsg(mdb->cmd,
-+ "INSERT INTO CurrentFile (FileId, BackupId, FullMark, MarkId) "
-+ " (SELECT FileId, %s, '%c', %s FROM File WHERE JobId=%s)",
-+ edit_int64(BackupId, ed1),
-+ JobLevel, ed2, ed2);
-+ stat = QUERY_DB(jcr, mdb, mdb->cmd);
-+ db_unlock(mdb);
-+ return stat;
-+}
-+
-+int db_accurate_create_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId)
-+{
-+ int stat;
-+ char ed1[50];
-+ db_lock(mdb);
-+ Mmsg(mdb->cmd, "CREATE TABLE ToBackup%s (name text)", edit_int64(JobId, ed1));
-+// Mmsg(mdb->cmd, "CREATE TEMPORARY TABLE ToBackup%s (name text)", edit_int64(JobId, ed1));
-+ stat = QUERY_DB(jcr, mdb, mdb->cmd);
-+ db_unlock(mdb);
-+ return stat;
-+}
-+
-+int db_accurate_drop_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId)
-+{
-+ int stat=0;
-+ char ed1[50];
-+ db_lock(mdb);
-+// Mmsg(mdb->cmd, "DROP TABLE ToBackup%s", edit_int64(JobId, ed1));
-+// stat = QUERY_DB(jcr, mdb, mdb->cmd);
-+ db_unlock(mdb);
-+ return stat;
-+}
-+
-+
-+/* Mark the file record as being visited during database
-+ * accurate compare. Stuff JobId into the MarkId field
-+ */
-+int db_accurate_mark_file_record(JCR *jcr, B_DB *mdb, JobId_t BackupId, FileId_t FileId, JobId_t JobId)
-+{
-+ int stat;
-+ char ed1[50], ed2[50], ed3[50];
-+
-+ db_lock(mdb);
-+ Mmsg(mdb->cmd, "UPDATE CurrentFile SET MarkId=%s WHERE FileId=%s AND BackupId=%s",
-+ edit_int64(JobId, ed1), edit_int64(FileId, ed2), edit_int64(BackupId, ed3));
-+ stat = QUERY_DB(jcr, mdb, mdb->cmd);
-+ if (!stat || sql_affected_rows(mdb) != 1) {
-+ stat = 0;
-+ }
-+ db_unlock(mdb);
-+ return stat;
-+}
-+
- /*
- * Update the Job record at start of Job
- *
-Index: src/cats/drop_postgresql_tables.in
-===================================================================
---- src/cats/drop_postgresql_tables.in (révision 6372)
-+++ src/cats/drop_postgresql_tables.in (copie de travail)
-@@ -5,7 +5,7 @@
- bindir=@SQL_BINDIR@
- db_name=@db_name@
-
--$bindir/psql -f - -d ${db_name} $* <<END-OF-DATA
-+$bindir/psql -f - -U regress -d ${db_name} $* <<END-OF-DATA
- drop table unsavedfiles;
- drop table basefiles;
- drop table jobmedia;
-Index: src/cats/make_postgresql_tables.in
-===================================================================
---- src/cats/make_postgresql_tables.in (révision 6372)
-+++ src/cats/make_postgresql_tables.in (copie de travail)
-@@ -5,7 +5,7 @@
- bindir=@SQL_BINDIR@
- db_name=@db_name@
-
--$bindir/psql -f - -d ${db_name} $* <<END-OF-DATA
-+$bindir/psql -f - -U regress -d ${db_name} $* <<END-OF-DATA
-
- CREATE TABLE filename
- (
-@@ -43,6 +43,59 @@
- CREATE INDEX file_jobid_idx on file (jobid);
- CREATE INDEX file_fp_idx on file (filenameid, pathid);
-
-+CREATE TABLE CurrentBackupId
-+(
-+ BackupId serial not null,
-+ ClientId integer not null,
-+ JobName text not null,
-+ FileSetId integer not null,
-+ primary key (BackupId)
-+);
-+
-+-- Serait bien de prendre la meme table pour
-+-- les File et le CurrentBackup...
-+-- Mais y'a des problemes pour les prunes
-+
-+CREATE TABLE CurrentFile
-+(
-+ FileId integer not null,
-+ BackupId integer not null,
-+ FullMark char(1) default 0,
-+ MarkId integer default 0,
-+ primary key (FileId)
-+);
-+
-+CREATE INDEX currentfile_fileid on CurrentFile (BackupId);
-+
-+-- CREATE TEMPORARY TABLE batch (fileindex int,
-+-- jobid int,
-+-- path varchar,
-+-- name varchar,
-+-- lstat varchar,
-+-- md5 varchar);
-+--
-+-- -- On batch insert dans la table temporaire
-+
-+-- il faut trouver les fichiers manquant
-+-- INSERT des nouveaux, UPDATE des anciens, SELECT pour trouver les deletes
-+
-+
-+-- il faut trouver les fichiers modifies
-+-- Le champs LStat n'est plus le meme
-+-- SELECT *
-+-- FROM CurrentBackup,
-+-- batch JOIN Path USING (Path) JOIN Filename USING (Name)
-+-- WHERE Path.PathId = CurrentBackup.PathId
-+-- AND Filename.FilenameId = CurrentBackup.FilenameId
-+-- AND CurrentBackup.LStat != batch.LStat
-+--
-+-- il faut mettre a jour la liste des fichiers
-+
-+
-+
-+
-+
-+
- --
- -- Possibly add one or more of the following indexes
- -- if your Verifies are too slow.
-Index: src/cats/protos.h
-===================================================================
---- src/cats/protos.h (révision 6372)
-+++ src/cats/protos.h (copie de travail)
-@@ -78,14 +78,17 @@
- /* sql_delete.c */
- int db_delete_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pool_dbr);
- int db_delete_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr);
-+int db_accurate_clean_deleted_files(JCR *jcr, B_DB *mdb, JobId_t JobId, JobId_t BackupId);
-
- /* sql_find.c */
- bool db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime);
- bool db_find_last_jobid(JCR *jcr, B_DB *mdb, const char *Name, JOB_DBR *jr);
-+JobId_t db_accurate_find_backupid(JCR *jcr, B_DB *mdb, JOB_DBR *jr);
- int db_find_next_volume(JCR *jcr, B_DB *mdb, int index, bool InChanger, MEDIA_DBR *mr);
- bool db_find_failed_job_since(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM *stime, int &JobLevel);
-
- /* sql_get.c */
-+int db_accurate_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JobId_t backupid, FILE_DBR *fdbr);
- bool db_get_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pdbr);
- int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cr);
- bool db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr);
-@@ -129,6 +132,14 @@
- int db_update_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr);
- int db_add_digest_to_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, char *digest, int type);
- int db_mark_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t JobId);
-+int db_accurate_mark_file_for_backup(JCR *jcr, B_DB *mdb, char *fname, FileId_t JobId);
-+int db_accurate_mark_file_record(JCR *jcr, B_DB *mdb, JobId_t BackupId, FileId_t FileId, JobId_t JobId);
-+int db_accurate_drop_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId);
-+int db_accurate_create_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId);
-+int db_accurate_delete_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t BackupId);
- void db_make_inchanger_unique(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr);
-+int db_accurate_cleanup_currentfile(JCR *jcr, B_DB *mdb, JobId_t BackupId);
-+int db_accurate_update_currentfile(JCR *jcr, B_DB *mdb, JobId_t JobId, int JobLevel, JobId_t BackupId);
-
-+
- #endif /* __SQL_PROTOS_H */
-Index: src/cats/sql_find.c
-===================================================================
---- src/cats/sql_find.c (révision 6372)
-+++ src/cats/sql_find.c (copie de travail)
-@@ -190,7 +190,64 @@
- return true;
- }
-
-+/*
-+ * Find BackupId of last job that ran. E.g. for
-+ *
-+ * Returns: Last backuip
-+ *
-+ */
-+JobId_t
-+db_accurate_find_backupid(JCR *jcr, B_DB *mdb, JOB_DBR *jr)
-+{
-+ SQL_ROW row;
-+ char ed1[50],ed2[50];
-+ JobId_t backupid=0;
-
-+ if (jcr->accurate == false) {
-+ return 0;
-+ }
-+
-+ /* Find backupid */
-+ db_lock(mdb);
-+ Dmsg2(100, "JobLevel=%d JobType=%d\n", jcr->JobLevel, jcr->JobType);
-+ Mmsg(mdb->cmd,
-+"SELECT BackupId FROM CurrentBackupId WHERE JobName='%s' AND "
-+"ClientId=%s AND FileSetId=%s ORDER BY BackupId DESC LIMIT 1",
-+ jr->Name,
-+ edit_int64(jr->ClientId, ed1),
-+ edit_int64(jr->FileSetId, ed2));
-+
-+ Dmsg1(100, "Query: %s\n", mdb->cmd);
-+ if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
-+ db_unlock(mdb);
-+ return 0;
-+ }
-+ if ((row = sql_fetch_row(mdb)) == NULL) {
-+ sql_free_result(mdb);
-+ if (jcr->JobLevel == L_FULL) {
-+ Mmsg(mdb->cmd,
-+ "INSERT INTO CurrentBackupId (JobName, ClientId, FileSetId) VALUES ('%s', %s, %s)",
-+ jr->Name, ed1, ed2);
-+ if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
-+ db_unlock(mdb);
-+ return 0;
-+ }
-+ backupid = sql_insert_id(mdb, NT_("CurrentBackupId"));
-+ } else {
-+ Mmsg1(&mdb->errmsg, _("No Job found for: %s.\n"), mdb->cmd);
-+ backupid = 0;
-+ }
-+ } else {
-+ backupid = str_to_int64(row[0]);
-+ }
-+
-+ sql_free_result(mdb);
-+
-+ db_unlock(mdb);
-+ return backupid;
-+}
-+
-+
- /*
- * Find JobId of last job that ran. E.g. for
- * VERIFY_CATALOG we want the JobId of the last INIT.
-Index: src/cats/sql_delete.c
-===================================================================
---- src/cats/sql_delete.c (révision 6372)
-+++ src/cats/sql_delete.c (copie de travail)
-@@ -236,5 +236,22 @@
- return 1;
- }
-
-+/*
-+ * Purge delete file from CurrentFile table. This table contains only
-+ * current files.
-+ */
-+int db_accurate_clean_deleted_files(JCR *jcr, B_DB *mdb, JobId_t JobId, JobId_t BackupId)
-+{
-+ int stat;
-+ char ed1[50], ed2[50];
-+ db_lock(mdb);
-+ Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE MarkId!=%s AND BackupId=%s",
-+ edit_int64(JobId, ed1), edit_int64(BackupId, ed2));
-+ stat = QUERY_DB(jcr, mdb, mdb->cmd);
-+ db_unlock(mdb);
-+ return stat;
-
-+}
-+
-+
- #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/
-Index: src/cats/sql_create.c
-===================================================================
---- src/cats/sql_create.c (révision 6372)
-+++ src/cats/sql_create.c (copie de travail)
-@@ -829,6 +829,14 @@
- return true;
- }
-
-+bool db_accurate_insert(JCR *jcr, B_DB *mdb, bool saved, const char *fname, struct stat *stat)
-+{
-+ int len;
-+ split_path_and_file(jcr, mdb, fname);
-+ /* make like in Verify code */
-+ return true;
-+}
-+
- /*
- * Create File record in B_DB
- *
-Index: src/cats/sql_get.c
-===================================================================
---- src/cats/sql_get.c (révision 6372)
-+++ src/cats/sql_get.c (copie de travail)
-@@ -66,6 +66,8 @@
- *
- * Returns: 0 on failure
- * 1 on success with the File record in FILE_DBR
-+ *
-+ * TODO: optimize this with only one query
- */
- int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JOB_DBR *jr, FILE_DBR *fdbr)
- {
-@@ -86,7 +88,73 @@
- return stat;
- }
-
-+/*
-+ * Given a full filename (with path), look up the File record
-+ * (with attributes) in the database.
-+ *
-+ * Returns: 0 on failure
-+ * 1 on success with the File record in FILE_DBR
-+ */
-+int db_accurate_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname,
-+ JobId_t backupid, FILE_DBR *fdbr)
-+{
-+ int stat=0;
-+ char ed1[50];
-+ SQL_ROW row;
-
-+ db_lock(mdb);
-+ split_path_and_file(jcr, mdb, fname);
-+
-+ mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->fnl+2);
-+ db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
-+
-+ mdb->esc_path = check_pool_memory_size(mdb->esc_path, 2*mdb->pnl+2);
-+ db_escape_string(jcr, mdb, mdb->esc_path, mdb->path, mdb->pnl);
-+
-+ /* A file could be present more than one time in the same backup, so we use LIMIT 1 */
-+ Mmsg(mdb->cmd,
-+"SELECT FileId, LStat, MD5, FilenameId, PathId, FileIndex, CurrentFile.MarkId, JobId "
-+ "FROM File JOIN CurrentFile USING (FileId) "
-+ "JOIN Filename USING (FilenameId) "
-+ "JOIN Path USING (PathId) "
-+ "WHERE Path.Path='%s' "
-+ "AND Filename.Name='%s' "
-+ "AND BackupId=%s "
-+ "ORDER BY FileId DESC LIMIT 1",
-+ mdb->esc_path,
-+ mdb->esc_name,
-+ edit_int64(backupid, ed1));
-+
-+ Dmsg1(2,"get_file %s\n", mdb->cmd);
-+
-+ if (QUERY_DB(jcr, mdb, mdb->cmd)) {
-+ char ed1[30];
-+ mdb->num_rows = sql_num_rows(mdb);
-+ if (mdb->num_rows == 1) {
-+ if ((row = sql_fetch_row(mdb)) == NULL) {
-+ Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
-+ } else {
-+ fdbr->FileId = str_to_int64(row[0]);
-+ bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat));
-+ bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest));
-+ fdbr->FilenameId = str_to_int64(row[3]);
-+ fdbr->PathId = str_to_int64(row[4]);
-+ fdbr->FileIndex = str_to_int64(row[5]);
-+ fdbr->MarkId = str_to_int64(row[6]);
-+ fdbr->JobId = str_to_int64(row[7]);
-+ stat=1;
-+ }
-+ }
-+ sql_free_result(mdb);
-+ } else {
-+ Mmsg(mdb->errmsg, _("File record: %s not found in Catalog for BackupId=%s.\n"), fname, ed1);
-+ }
-+
-+ db_unlock(mdb);
-+
-+ return stat;
-+}
-+
- /*
- * Get a File record
- * Returns: 0 on failure
-Index: src/stored/append.c
-===================================================================
---- src/stored/append.c (révision 6372)
-+++ src/stored/append.c (copie de travail)
-@@ -185,16 +185,18 @@
-
- Dmsg2(890, "<filed: Header FilInx=%d stream=%d\n", file_index, stream);
-
-- 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 != 0) { /* TODO: handle file_index == 0 */
-+ 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;
-- last_file_index = file_index;
-- }
-
- /* Read data stream from the File daemon.
- * The data stream is just raw bytes
-@@ -212,24 +214,26 @@
- stream_to_ascii(buf1, 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);
-+ if (file_index != 0) {
-+ 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 ||
-Index: src/findlib/find.h
-===================================================================
---- src/findlib/find.h (révision 6372)
-+++ 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;
-Index: patches/testing/project-accurate-backup.patch
-===================================================================
---- patches/testing/project-accurate-backup.patch (révision 6410)
-+++ patches/testing/project-accurate-backup.patch (copie de travail)
-@@ -75,7 +75,7 @@
-
- /* Commands sent to File daemon */
- static char backupcmd[] = "backup\n";
--@@ -96,7 +97,443 @@
-+@@ -96,7 +97,450 @@
- return true;
- }
-
-@@ -93,6 +93,11 @@
- + return 0;
- +}
- +
-++bool db_get_jobids(JCR *jcr)
-++{
-++
-++}
-++
- +bool accurate_send_current_files(JCR *jcr)
- +{
- + char buf[MAXSTRING];
-@@ -108,6 +113,7 @@
- + 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)
-@@ -299,6 +305,15 @@
- + char ed1[50], ed2[50];
- +
- + bsnprintf(buf, sizeof(buf),
-++ "SELECT Name FROM ToBackup%s",
-++ edit_uint64(jcr->JobId, ed2));
-++
-++ /* missing_handler is called for each file found */
-++ Dmsg1(2, "display files to backup cmd=%s\n", buf);
-++ db_sql_query(jcr->db, buf, accurate_handler, (void *)jcr);
-++ jcr->file_bsock->signal(BNET_EOD);
-++
-++ bsnprintf(buf, sizeof(buf),
- + "SELECT Path.Path,Filename.Name "
- + "FROM CurrentFile "
- + "JOIN File USING (FileId) "
-@@ -312,14 +327,6 @@
- + db_sql_query(jcr->db, buf, accurate_handler, (void *)jcr);
- + jcr->file_bsock->signal(BNET_EOD);
- +
--+ bsnprintf(buf, sizeof(buf),
--+ "SELECT Name FROM ToBackup%s",
--+ edit_uint64(jcr->JobId, ed2));
--+ /* missing_handler is called for each file found */
--+ Dmsg1(2, "display files to backup cmd=%s\n", buf);
--+ db_sql_query(jcr->db, buf, accurate_handler, (void *)jcr);
--+ jcr->file_bsock->signal(BNET_EOD);
--+
- + return 1;
- +}
- +
-@@ -519,7 +526,7 @@
- * Do a backup of the specified FileSet
- *
- * Returns: false on failure
--@@ -231,9 +668,18 @@
-+@@ -231,9 +675,18 @@
- goto bail_out;
- }
-
-@@ -538,18 +545,6 @@
- if (stat == JS_Terminated) {
- backup_cleanup(jcr, stat);
- return true;
--Index: src/dird/job.c
--===================================================================
----- src/dird/job.c (révision 6372)
--+++ src/dird/job.c (copie de travail)
--@@ -979,6 +979,7 @@
-- jcr->spool_data = job->spool_data;
-- jcr->spool_size = job->spool_size;
-- jcr->write_part_after_job = job->write_part_after_job;
--+ jcr->accurate = job->accurate;
-- if (jcr->RestoreBootstrap) {
-- free(jcr->RestoreBootstrap);
-- jcr->RestoreBootstrap = NULL;
- Index: src/dird/inc_conf.c
- ===================================================================
- --- src/dird/inc_conf.c (révision 6372)
-@@ -589,49 +584,15 @@
- {NULL, 0, 0}
- };
-
--Index: src/dird/dird_conf.c
--===================================================================
----- src/dird/dird_conf.c (révision 6372)
--+++ src/dird/dird_conf.c (copie de travail)
--@@ -319,6 +319,7 @@
-- {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
-- {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
-- {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
--+ {"accurate", store_bool, ITEM(res_job.accurate), 0,0,0},
-- {NULL, NULL, {0}, 0, 0, 0}
-- };
--
--@@ -618,6 +619,9 @@
-- if (res->res_job.spool_size) {
-- sendit(sock, _(" SpoolSize=%s\n"), edit_uint64(res->res_job.spool_size, ed1));
-- }
--+ if (res->res_job.JobType == JT_BACKUP) {
--+ sendit(sock, _(" Accurate=%d\n"), res->res_job.accurate);
--+ }
-- if (res->res_job.JobType == JT_MIGRATE) {
-- sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
-- }
--Index: src/dird/dird_conf.h
--===================================================================
----- src/dird/dird_conf.h (révision 6372)
--+++ src/dird/dird_conf.h (copie de travail)
--@@ -400,6 +400,7 @@
-- bool write_part_after_job; /* Set to write part after job in SD */
-- bool enabled; /* Set if job enabled */
-- bool OptimizeJobScheduling; /* Set if we should optimize Job scheduling */
--+ bool accurate; /* Set if it is an accurate backup job */
--
-- MSGS *messages; /* How and where to send messages */
-- SCHED *schedule; /* When -- Automatic schedule */
- Index: src/filed/backup.c
- ===================================================================
- --- src/filed/backup.c (révision 6372)
- +++ src/filed/backup.c (copie de travail)
--@@ -48,8 +48,113 @@
-+@@ -48,8 +48,114 @@
- 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_files(JCR *jcr, char *fname);
-++static bool encode_and_send_deleted_file(JCR *jcr, char *fname);
-
- /*
- + * Called by save_file when accept/discard file for backup
-@@ -704,16 +665,17 @@
- + return true;
- + }
- +
--+ /* get deleted files */
--+ while (dir->recv() >= 0) {
--+ Dmsg1(1, "deleted = %s\n", dir->msg);
--+ encode_and_send_deleted_files(jcr, dir->msg);
--+ }
- + /* get missing files */
- + while (dir->recv() >= 0) {
- + Dmsg1(1, "missing = %s\n", dir->msg);
- + }
--+
-++
-++ /* get deleted files */
-++ while (dir->recv() >= 0) {
-++ Dmsg1(1, "deleted = %s\n", dir->msg);
-++ encode_and_send_deleted_file(jcr, dir->msg);
-++ }
-++
- + return true;
- +}
- +
-@@ -741,7 +703,7 @@
- * Find all the requested files and send them
- * to the Storage daemon.
- *
--@@ -66,7 +171,6 @@
-+@@ -66,7 +172,6 @@
- BSOCK *sd;
- bool ok = true;
- // TODO landonf: Allow user to specify encryption algorithm
-@@ -749,7 +711,7 @@
- sd = jcr->store_bsock;
-
- set_jcr_job_status(jcr, JS_Running);
--@@ -134,6 +238,20 @@
-+@@ -134,6 +239,20 @@
- ok = false; /* error */
- set_jcr_job_status(jcr, JS_ErrorTerminated);
- }
-@@ -770,7 +732,7 @@
-
- free_pool_memory(jcr->acl_text);
-
--@@ -355,9 +473,11 @@
-+@@ -355,9 +474,11 @@
- case FT_DIRNOCHG:
- case FT_NOCHG:
- Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname);
-@@ -782,7 +744,7 @@
- return 1;
- case FT_NOOPEN: {
- berrno be;
--@@ -1111,6 +1231,9 @@
-+@@ -1111,6 +1232,9 @@
- }
- unstrip_path(ff_pkt);
-
-@@ -792,11 +754,11 @@
- 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"),
--@@ -1121,6 +1244,58 @@
-+@@ -1121,6 +1245,58 @@
- return true;
- }
-
--+static bool encode_and_send_deleted_files(JCR *jcr, char *fname)
-++static bool encode_and_send_deleted_file(JCR *jcr, char *fname)
- +{
- + BSOCK *sd = jcr->store_bsock;
- + char *attribs;
-@@ -1402,18 +1364,6 @@
-
- /* Send attributes and digest to Director for Catalog */
- if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX ||
--Index: src/jcr.h
--===================================================================
----- src/jcr.h (révision 6372)
--+++ src/jcr.h (copie de travail)
--@@ -208,6 +208,7 @@
-- B_DB *db_batch; /* database pointer for batch insert */
-- ATTR_DBR *ar; /* DB attribute record */
-- guid_list *id_list; /* User/group id to name list */
--+ bool accurate; /* true if job is accurate */
--
-- void *plugin_ctx_list; /* list of contexts for plugins */
-- void *plugin_ctx; /* current plugin context */
- Index: src/findlib/find.h
- ===================================================================
- --- src/findlib/find.h (révision 6372)
+++ /dev/null
-Index: src/dird/fd_cmds.c
-===================================================================
---- src/dird/fd_cmds.c (révision 6471)
-+++ src/dird/fd_cmds.c (copie de travail)
-@@ -50,7 +50,7 @@
- static char filesetcmd[] = "fileset%s\n"; /* set full fileset */
- static char jobcmd[] = "JobId=%s Job=%s SDid=%u SDtime=%u Authorization=%s\n";
- /* Note, mtime_only is not used here -- implemented as file option */
--static char levelcmd[] = "level = %s%s mtime_only=%d\n";
-+static char levelcmd[] = "level = %s%s%s mtime_only=%d\n";
- static char runscript[] = "Run OnSuccess=%u OnFailure=%u AbortOnError=%u When=%u Command=%s\n";
- static char runbeforenow[]= "RunBeforeNow\n";
-
-@@ -226,13 +226,12 @@
- char ed1[50];
-
- stime = str_to_utime(jcr->stime);
-- fd->fsend(levelcmd, NT_("since_utime "), edit_uint64(stime, ed1), 0);
-+ fd->fsend(levelcmd, "", NT_("since_utime "), edit_uint64(stime, ed1), 0);
- while (bget_dirmsg(fd) >= 0) { /* allow him to poll us to sync clocks */
- Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg);
- }
- }
-
--
- /*
- * 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, 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, not_accurate, "full", " ", 0);
- break;
- case L_DIFFERENTIAL:
-- fd->fsend(levelcmd, "differential", " ", 0);
-+ fd->fsend(levelcmd, accurate, "differential", " ", 0);
- send_since_time(jcr);
- break;
- case L_INCREMENTAL:
-- fd->fsend(levelcmd, "incremental", " ", 0);
-+ fd->fsend(levelcmd, accurate, "incremental", " ", 0);
- send_since_time(jcr);
- break;
- case L_SINCE:
-Index: src/dird/backup.c
-===================================================================
---- src/dird/backup.c (révision 6471)
-+++ src/dird/backup.c (copie de travail)
-@@ -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;
-+
-+ if (job_canceled(jcr)) {
-+ return 1;
-+ }
-+
-+ 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;
-+}
-+
-+/*
-+ * 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 send_accurate_current_files(JCR *jcr)
-+{
-+ POOL_MEM buf;
-+
-+ 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, jcr->db, &jcr->jr, jobids);
-+
-+ 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;
-+}
-+
-+/*
- * Do a backup of the specified FileSet
- *
- * Returns: false on failure
-@@ -225,6 +284,14 @@
- Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
- }
-
-+ /*
-+ * If backup is in accurate mode, we send the list of
-+ * all files to FD.
-+ */
-+ if (!send_accurate_current_files(jcr)) {
-+ goto bail_out;
-+ }
-+
- /* Send backup command */
- fd->fsend(backupcmd);
- if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
-@@ -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/catreq.c
-===================================================================
---- src/dird/catreq.c (révision 6471)
-+++ src/dird/catreq.c (copie de travail)
-@@ -346,8 +346,8 @@
- * Update File Attributes in the catalog with data
- * sent by the Storage daemon. Note, we receive the whole
- * attribute record, but we select out only the stat packet,
-- * VolSessionId, VolSessionTime, FileIndex, and file name
-- * to store in the catalog.
-+ * VolSessionId, VolSessionTime, FileIndex, file type, and
-+ * file name to store in the catalog.
- */
- void catalog_update(JCR *jcr, BSOCK *bs)
- {
-@@ -357,6 +357,7 @@
- uint32_t FileIndex;
- uint32_t data_len;
- char *p;
-+ int filetype;
- int len;
- char *fname, *attr;
- ATTR_DBR *ar = NULL;
-@@ -415,6 +416,7 @@
- p = jcr->attr - bs->msg + p; /* point p into jcr->attr */
- skip_nonspaces(&p); /* skip FileIndex */
- skip_spaces(&p);
-+ filetype = str_to_int32(p); /* TODO: choose between unserialize and str_to_int32 */
- skip_nonspaces(&p); /* skip FileType */
- skip_spaces(&p);
- fname = p;
-@@ -425,7 +427,11 @@
- Dmsg1(400, "dird<stored: attr=%s\n", attr);
- ar->attr = attr;
- ar->fname = fname;
-- ar->FileIndex = FileIndex;
-+ if (filetype == FT_DELETED) {
-+ ar->FileIndex = 0; /* special value */
-+ } else {
-+ ar->FileIndex = FileIndex;
-+ }
- ar->Stream = Stream;
- ar->link = NULL;
- if (jcr->mig_jcr) {
-Index: src/dird/ua_restore.c
-===================================================================
---- src/dird/ua_restore.c (révision 6471)
-+++ 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];
-
-- 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/dird/inc_conf.c
-===================================================================
---- src/dird/inc_conf.c (révision 6471)
-+++ src/dird/inc_conf.c (copie de travail)
-@@ -96,6 +96,7 @@
- static RES_ITEM options_items[] = {
- {"compression", store_opts, {0}, 0, 0, 0},
- {"signature", store_opts, {0}, 0, 0, 0},
-+ {"accurate", store_opts, {0}, 0, 0, 0},
- {"verify", store_opts, {0}, 0, 0, 0},
- {"onefs", store_opts, {0}, 0, 0, 0},
- {"recurse", store_opts, {0}, 0, 0, 0},
-@@ -137,6 +138,7 @@
- INC_KW_DIGEST,
- INC_KW_ENCRYPTION,
- INC_KW_VERIFY,
-+ INC_KW_ACCURATE,
- INC_KW_ONEFS,
- INC_KW_RECURSE,
- INC_KW_SPARSE,
-@@ -167,6 +169,7 @@
- {"signature", INC_KW_DIGEST},
- {"encryption", INC_KW_ENCRYPTION},
- {"verify", INC_KW_VERIFY},
-+ {"accurate", INC_KW_ACCURATE},
- {"onefs", INC_KW_ONEFS},
- {"recurse", INC_KW_RECURSE},
- {"sparse", INC_KW_SPARSE},
-@@ -278,6 +281,12 @@
- bstrncat(opts, lc->str, optlen);
- bstrncat(opts, ":", optlen); /* terminate it */
- Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
-+ } else if (keyword == INC_KW_ACCURATE) { /* special case */
-+ /* ***FIXME**** ensure these are in permitted set */
-+ bstrncat(opts, "C", optlen); /* indicate Accurate */
-+ bstrncat(opts, lc->str, optlen);
-+ bstrncat(opts, ":", optlen); /* terminate it */
-+ Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
- } else if (keyword == INC_KW_STRIPPATH) { /* another special case */
- if (!is_an_integer(lc->str)) {
- scan_err1(lc, _("Expected a strip path positive integer, got:%s:"), lc->str);
-Index: src/filed/backup.c
-===================================================================
---- src/filed/backup.c (révision 6484)
-+++ src/filed/backup.c (copie de travail)
-@@ -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);
-@@ -49,7 +50,255 @@
- static void crypto_session_end(JCR *jcr);
- static bool crypto_session_send(JCR *jcr, BSOCK *sd);
-
-+typedef struct CurFile {
-+ hlink link;
-+ char *fname;
-+ char *lstat;
-+ bool seen;
-+} CurFile;
-+
-+#define accurate_mark_file_as_seen(elt) ((elt)->seen = 1)
-+#define accurate_file_has_been_seen(elt) ((elt)->seen)
-+
- /*
-+ * This function is called for each file seen in fileset.
-+ * We check in file_list hash if fname have been backuped
-+ * the last time. After we can compare Lstat field.
-+ *
-+ */
-+/* TODO: tweak verify code to use the same function ?? */
-+bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
-+{
-+ char *p;
-+ int stat=false;
-+ struct stat statc; /* catalog stat */
-+ char *Opts_Digest;
-+ char *fname;
-+ CurFile *elt;
-+
-+ int32_t LinkFIc;
-+
-+ if (*ff_pkt->VerifyOpts) { /* use mtime + ctime checks by default */
-+ Opts_Digest = ff_pkt->VerifyOpts;
-+ } else {
-+ Opts_Digest = "cm";
-+ }
-+
-+ if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
-+ return true;
-+ }
-+
-+ strip_path(ff_pkt);
-+
-+ if (S_ISDIR(ff_pkt->statp.st_mode)) {
-+ fname = ff_pkt->link;
-+ } else {
-+ fname = ff_pkt->fname;
-+ }
-+
-+ elt = (CurFile *) jcr->file_list->lookup(fname);
-+
-+ if (!elt) {
-+ Dmsg1(500, "accurate %s = yes (not found)\n", fname);
-+ stat=true;
-+ goto bail_out;
-+ }
-+
-+ if (accurate_file_has_been_seen(elt)) {
-+ Dmsg1(500, "accurate %s = no (already seen)\n", fname);
-+ stat=false;
-+ goto bail_out;
-+ }
-+
-+ decode_stat(elt->lstat, &statc, &LinkFIc); /* decode catalog stat */
-+// *do_Digest = CRYPTO_DIGEST_NONE;
-+
-+ for (p=Opts_Digest; *p; p++) {
-+ char ed1[30], ed2[30];
-+ switch (*p) {
-+ case 'i': /* compare INODEs */
-+ 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_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_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_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_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_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_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_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;
-+ case '1': /* compare SHA1 */
-+// *do_Digest = CRYPTO_DIGEST_SHA1;
-+ break;
-+ case ':':
-+ case 'V':
-+ default:
-+ break;
-+ }
-+ }
-+ accurate_mark_file_as_seen(elt);
-+ Dmsg2(500, "accurate %s = %i\n", fname, stat);
-+
-+bail_out:
-+ unstrip_path(ff_pkt);
-+ return stat;
-+}
-+
-+/*
-+ * 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) {
-+ return true;
-+ }
-+
-+ if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
-+ 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);
-+
-+ /* 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;
-+ elt->seen=0;
-+ jcr->file_list->insert(elt->fname, elt);
-+ Dmsg2(500, "add fname=%s lstat=%s\n", elt->fname, elt->lstat);
-+ }
-+ }
-+
-+// jcr->file_list->stats();
-+ /* TODO: send a EOM ?
-+ dir->fsend("2000 OK accurate\n");
-+ */
-+ return true;
-+}
-+
-+bool accurate_send_deleted_list(JCR *jcr)
-+{
-+ CurFile *elt;
-+ FF_PKT *ff_pkt;
-+
-+ int stream = STREAM_UNIX_ATTRIBUTES;
-+
-+ if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
-+ goto bail_out;
-+ }
-+
-+ if (jcr->file_list == NULL) {
-+ goto bail_out;
-+ }
-+
-+ ff_pkt = init_find_files();
-+ ff_pkt->type = FT_DELETED;
-+
-+ foreach_htable (elt, jcr->file_list) {
-+ if (!accurate_file_has_been_seen(elt)) { /* already seen */
-+ Dmsg3(500, "deleted fname=%s lstat=%s seen=%i\n", elt->fname, elt->lstat, elt->seen);
-+ ff_pkt->fname = elt->fname;
-+ decode_stat(elt->lstat, &ff_pkt->statp, &ff_pkt->LinkFI); /* decode catalog stat */
-+ encode_and_send_attributes(jcr, ff_pkt, stream);
-+ }
-+// Free(elt->fname);
-+ }
-+ term_find_files(ff_pkt);
-+bail_out:
-+ /* 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;
-+ }
-+ return true;
-+}
-+
-+/*
- * Find all the requested files and send them
- * to the Storage daemon.
- *
-@@ -100,7 +349,7 @@
- */
- jcr->compress_buf_size = jcr->buf_size + ((jcr->buf_size+999) / 1000) + 30;
- jcr->compress_buf = get_memory(jcr->compress_buf_size);
--
-+
- #ifdef HAVE_LIBZ
- z_stream *pZlibStream = (z_stream*)malloc(sizeof(z_stream));
- if (pZlibStream) {
-@@ -121,10 +370,13 @@
- return false;
- }
-
-- 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 +387,8 @@
- set_jcr_job_status(jcr, JS_ErrorTerminated);
- }
-
-+ accurate_send_deleted_list(jcr); /* send deleted list to SD */
-+
- free_pool_memory(jcr->acl_text);
-
- stop_heartbeat_monitor(jcr);
-@@ -1102,7 +1356,9 @@
- * For a directory, link is the same as fname, but with trailing
- * slash. For a linked file, link is the link.
- */
-- strip_path(ff_pkt);
-+ if (ff_pkt->type != FT_DELETED) { /* already stripped */
-+ strip_path(ff_pkt);
-+ }
- if (ff_pkt->type == FT_LNK || ff_pkt->type == FT_LNKSAVED) {
- Dmsg2(300, "Link %s to %s\n", ff_pkt->fname, ff_pkt->link);
- stat = sd->fsend("%ld %d %s%c%s%c%s%c%s%c", jcr->JobFiles,
-@@ -1116,7 +1372,9 @@
- stat = sd->fsend("%ld %d %s%c%s%c%c%s%c", jcr->JobFiles,
- ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0, attribsEx, 0);
- }
-- unstrip_path(ff_pkt);
-+ if (ff_pkt->type != FT_DELETED) {
-+ unstrip_path(ff_pkt);
-+ }
-
- Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
- if (!stat) {
-Index: src/filed/job.c
-===================================================================
---- src/filed/job.c (révision 6471)
-+++ 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_cmd(JCR *jcr);
-
- /* Forward referenced functions */
- static int backup_cmd(JCR *jcr);
-@@ -106,6 +107,7 @@
- {"RunBeforeJob", runbefore_cmd, 0},
- {"RunAfterJob", runafter_cmd, 0},
- {"Run", runscript_cmd, 0},
-+ {"accurate", accurate_cmd, 0},
- {NULL, NULL} /* list terminator */
- };
-
-@@ -1057,6 +1059,16 @@
- }
- fo->VerifyOpts[j] = 0;
- break;
-+ case 'C': /* accurate options */
-+ /* Copy Accurate Options */
-+ for (j=0; *p && *p != ':'; p++) {
-+ fo->AccurateOpts[j] = *p;
-+ if (j < (int)sizeof(fo->AccurateOpts) - 1) {
-+ j++;
-+ }
-+ }
-+ fo->AccurateOpts[j] = 0;
-+ break;
- case 'P': /* strip path */
- /* Get integer */
- p++; /* skip P */
-@@ -1195,6 +1207,9 @@
-
- level = get_memory(dir->msglen+1);
- Dmsg1(110, "level_cmd: %s", dir->msg);
-+ if (strstr(dir->msg, "accurate")) {
-+ jcr->accurate = true;
-+ }
- if (sscanf(dir->msg, "level = %s ", level) != 1) {
- goto bail_out;
- }
-@@ -1204,14 +1219,14 @@
- /* Full backup requested? */
- } else if (strcmp(level, "full") == 0) {
- jcr->JobLevel = L_FULL;
-- } else if (strcmp(level, "differential") == 0) {
-+ } else if (strstr(level, "differential")) {
- jcr->JobLevel = L_DIFFERENTIAL;
- free_memory(level);
- return 1;
-- } else if (strcmp(level, "incremental") == 0) {
-+ } else if (strstr(level, "incremental")) {
- jcr->JobLevel = L_INCREMENTAL;
- free_memory(level);
-- return 1;
-+ return 1;
- /*
- * We get his UTC since time, then sync the clocks and correct it
- * to agree with our clock.
-Index: src/filed/restore.c
-===================================================================
---- src/filed/restore.c (révision 6471)
-+++ src/filed/restore.c (copie de travail)
-@@ -320,6 +320,11 @@
- bclose(&rctx.bfd);
- }
-
-+ /* TODO: manage deleted files */
-+ if (rctx.type == FT_DELETED) { /* deleted file */
-+ continue;
-+ }
-+
- /*
- * Unpack attributes and do sanity check them
- */
-Index: src/cats/protos.h
-===================================================================
---- src/cats/protos.h (révision 6471)
-+++ 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 6471)
-+++ 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/baconfig.h
-===================================================================
---- src/baconfig.h (révision 6471)
-+++ src/baconfig.h (copie de travail)
-@@ -277,6 +277,7 @@
- #define FT_INVALIDDT 20 /* Drive type not allowed for */
- #define FT_REPARSE 21 /* Win NTFS reparse point */
- #define FT_PLUGIN 22 /* Plugin generated filename */
-+#define FT_DELETED 23 /* Deleted file entry */
-
- /* Definitions for upper part of type word (see above). */
- #define AR_DATA_STREAM (1<<16) /* Data stream id present */
-Index: src/stored/bextract.c
-===================================================================
---- src/stored/bextract.c (révision 6471)
-+++ src/stored/bextract.c (copie de travail)
-@@ -343,6 +343,12 @@
-
- build_attr_output_fnames(jcr, attr);
-
-+ if (attr->type == FT_DELETED) { /* TODO: choose the right fname/ofname */
-+ Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname);
-+ extract = false;
-+ return true;
-+ }
-+
- extract = false;
- stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
- switch (stat) {
-Index: src/stored/bscan.c
-===================================================================
---- src/stored/bscan.c (révision 6471)
-+++ src/stored/bscan.c (copie de travail)
-@@ -845,7 +845,11 @@
- ar.ClientId = mjcr->ClientId;
- ar.JobId = mjcr->JobId;
- ar.Stream = rec->Stream;
-- ar.FileIndex = rec->FileIndex;
-+ if (type == FT_DELETED) {
-+ ar.FileIndex = 0;
-+ } else {
-+ ar.FileIndex = rec->FileIndex;
-+ }
- ar.attr = ap;
- if (dcr->VolFirstIndex == 0) {
- dcr->VolFirstIndex = rec->FileIndex;
-Index: src/jcr.h
-===================================================================
---- src/jcr.h (révision 6471)
-+++ src/jcr.h (copie de travail)
-@@ -119,6 +119,7 @@
-
- /* Forward referenced structures */
- class JCR;
-+class htable;
- struct FF_PKT;
- struct B_DB;
- struct ATTR_DBR;
-@@ -318,6 +319,7 @@
- CRYPTO_CTX crypto; /* Crypto ctx */
- DIRRES* director; /* Director resource */
- bool VSS; /* VSS used by FD */
-+ htable *file_list; /* Previous file list (accurate mode) */
- #endif /* FILE_DAEMON */
-
-
-Index: src/lib/Makefile.in
-===================================================================
---- src/lib/Makefile.in (révision 6471)
-+++ 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/lib/attr.c
-===================================================================
---- src/lib/attr.c (révision 6471)
-+++ src/lib/attr.c (copie de travail)
-@@ -242,6 +242,14 @@
- char *p, *f;
- guid_list *guid;
-
-+ if (attr->type == FT_DELETED) { /* TODO: change this to get last seen values */
-+ bsnprintf(buf, sizeof(buf),
-+ "---------- - - - - ---------- -------- %s\n", attr->ofname);
-+ Dmsg1(20, "%s", buf);
-+ Jmsg(jcr, M_RESTORED, 1, "%s", buf);
-+ return;
-+ }
-+
- if (!jcr->id_list) {
- jcr->id_list = new_guid_list();
- }
-Index: src/findlib/create_file.c
-===================================================================
---- src/findlib/create_file.c (révision 6471)
-+++ src/findlib/create_file.c (copie de travail)
-@@ -389,6 +389,9 @@
- return CF_CREATED;
- }
-
-+ case FT_DELETED:
-+ Qmsg2(jcr, M_INFO, 0, _("Original file %s have been deleted: type=%d\n"), attr->fname, attr->type);
-+ break;
- /* The following should not occur */
- case FT_NOACCESS:
- case FT_NOFOLLOW:
-Index: src/findlib/find.c
-===================================================================
---- src/findlib/find.c (révision 6471)
-+++ 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(1, "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 6471)
-+++ 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 false;
-+ }
-+
-+ return true;
-+}
-+
-+/*
- * Find a single file.
- * handle_file is the callback for handling the file.
- * p is the filename
-@@ -327,22 +354,18 @@
- }
- ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
- }
--
- /*
- * If this is an Incremental backup, see if file was modified
- * since our last "save_time", presumably the last Full save
- * or Incremental.
- */
-- if (ff_pkt->incremental && !S_ISDIR(ff_pkt->statp.st_mode)) {
-- 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);
-- }
-+ if ( ff_pkt->incremental
-+ && !S_ISDIR(ff_pkt->statp.st_mode)
-+ && !check_changes(jcr, ff_pkt))
-+ {
-+ Dmsg1(500, "Non-directory incremental: %s\n", ff_pkt->fname);
-+ ff_pkt->type = FT_NOCHG;
-+ return handle_file(jcr, ff_pkt, top_level);
- }
-
- #ifdef HAVE_DARWIN_OS
-@@ -502,10 +525,7 @@
- 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 {
-Index: src/findlib/find.h
-===================================================================
---- src/findlib/find.h (révision 6471)
-+++ src/findlib/find.h (copie de travail)
-@@ -146,6 +146,7 @@
- int GZIP_level; /* GZIP level */
- int strip_path; /* strip path count */
- char VerifyOpts[MAX_FOPTS]; /* verify options */
-+ char AccurateOpts[MAX_FOPTS]; /* accurate mode options */
- alist regex; /* regex string(s) */
- alist regexdir; /* regex string(s) for directories */
- alist regexfile; /* regex string(s) for files */
-@@ -215,6 +216,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 6471)
-+++ 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));
+++ /dev/null
-
-CREATE TABLE CurrentBackupId
-(
- BackupId serial not null,
- ClientId integer not null,
- JobName text not null,
- FileSetId integer not null,
- primary key (BackupId)
-);
-
--- Serait bien de prendre la meme table pour
--- les File et le CurrentBackup...
--- Quand on prune, il faut purger ici aussi
-
-CREATE TABLE CurrentBackup
-(
- FileId integer not null,
- BackupId integer not null,
- FullMark char(1) default 0,
- MarkId integer default 0,
- primary key (FileId)
-);
-
-CREATE INDEX currentbackup_fileid on CurrentBackup (BackupId);
-
--- On batch insert dans la table temporaire
-
--- il faut trouver les fichiers manquant
--- INSERT des nouveaux, UPDATE des anciens, SELECT pour trouver les deletes
-
--- UPDATE CurrentBackup SET MarkId = 1
--- WHERE CurrentBackup.Id IN (
--- SELECT CurrentBackup.Id
--- FROM batch
--- JOIN Filename USING (Name)
--- JOIN Path USING (Path)
--- JOIN CurrentBackup
--- ON (Filename.FilenameId = CurrentBackup.FilenameId
--- AND
--- Path.PathId = CurrentBackup.PathId)
--- WHERE CurrentBackup.BackupId = 1
--- )
---
--- INSERT INTO CurrentBackup (BackupId, FullMark, PathId, FilenameId, LStat, MD5)
--- (SELECT CurrentBackup.BackupId, 'F', Path.PathId, Filename.FilenameId,
--- batch.LStat, batch.MD5
--- FROM batch
--- JOIN Path USING (Path)
--- JOIN Filename USING (Name)
--- LEFT OUTER JOIN CurrentBackup
--- ON (Filename.FilenameId = CurrentBackup.FilenameId
--- AND Path.PathId = CurrentBackup.PathId
--- AND CurrentBackup.BackupId = 1)
--- WHERE BackupId IS NULL
---
--- )
---
--- il faut trouver les fichiers modifies
--- Le champs LStat n'est plus le meme
--- SELECT *
--- FROM batch
--- JOIN Path USING (Path)
--- JOIN Filename USING (Name)
--- JOIN CurrentBackup USING (FilenameId, PathId)
---
--- WHERE CurrentBackup.LStat != batch.LStat
--- AND CurrentBackup.BackupId = 1
---
--- il faut mettre a jour la liste des fichiers
-
+++ /dev/null
-Index: src/dird/msgchan.c
-===================================================================
---- src/dird/msgchan.c (revision 5108)
-+++ src/dird/msgchan.c (working copy)
-@@ -51,7 +51,7 @@
- /* Commands sent to Storage daemon */
- static char jobcmd[] = "JobId=%s job=%s job_name=%s client_name=%s "
- "type=%d level=%d FileSet=%s NoAttr=%d SpoolAttr=%d FileSetMD5=%s "
-- "SpoolData=%d WritePartAfterJob=%d PreferMountedVols=%d\n";
-+ "SpoolData=%d SpoolSize=%s WritePartAfterJob=%d PreferMountedVols=%d\n";
- static char use_storage[] = "use storage=%s media_type=%s pool_name=%s "
- "pool_type=%s append=%d copy=%d stripe=%d\n";
- static char use_device[] = "use device=%s\n";
-@@ -157,7 +157,7 @@
- POOL_MEM job_name, client_name, fileset_name;
- int copy = 0;
- int stripe = 0;
-- char ed1[30];
-+ char ed1[30], ed2[30];
-
- sd = jcr->store_bsock;
- /*
-@@ -186,8 +186,9 @@
- job_name.c_str(), client_name.c_str(),
- jcr->JobType, jcr->JobLevel,
- fileset_name.c_str(), !jcr->pool->catalog_files,
-- jcr->job->SpoolAttributes, jcr->fileset->MD5, jcr->spool_data,
-- jcr->write_part_after_job, jcr->job->PreferMountedVolumes);
-+ jcr->job->SpoolAttributes, jcr->fileset->MD5, jcr->spool_data,
-+ edit_int64(jcr->spool_size, ed2), jcr->write_part_after_job,
-+ jcr->job->PreferMountedVolumes);
- Dmsg1(100, ">stored: %s\n", sd->msg);
- if (bget_dirmsg(sd) > 0) {
- Dmsg1(100, "<stored: %s", sd->msg);
-Index: src/dird/job.c
-===================================================================
---- src/dird/job.c (revision 5108)
-+++ src/dird/job.c (working copy)
-@@ -927,6 +927,7 @@
- jcr->fileset = job->fileset;
- jcr->messages = job->messages;
- jcr->spool_data = job->spool_data;
-+ jcr->spool_size = job->spool_size;
- jcr->write_part_after_job = job->write_part_after_job;
- if (jcr->RestoreBootstrap) {
- free(jcr->RestoreBootstrap);
-Index: src/dird/dird_conf.c
-===================================================================
---- src/dird/dird_conf.c (revision 5108)
-+++ src/dird/dird_conf.c (working copy)
-@@ -295,6 +295,7 @@
- {"enabled", store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true},
- {"spoolattributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, false},
- {"spooldata", store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
-+ {"spoolsize", store_size, ITEM(res_job.spool_size), 0, 0, 0},
- {"rerunfailedlevels", store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
- {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
- {"runbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
-@@ -600,6 +601,9 @@
- res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
- edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
- res->res_job.spool_data, res->res_job.write_part_after_job);
-+ if (res->res_job.spool_size) {
-+ sendit(sock, _(" SpoolSize=%s\n"), edit_uint64(res->res_job.spool_size, ed1));
-+ }
- if (res->res_job.JobType == JT_MIGRATE) {
- sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
- }
-Index: src/dird/dird_conf.h
-===================================================================
---- src/dird/dird_conf.h (revision 5108)
-+++ src/dird/dird_conf.h (working copy)
-@@ -380,6 +380,7 @@
- utime_t RescheduleInterval; /* Reschedule interval */
- utime_t JobRetention; /* job retention period in seconds */
- uint32_t MaxConcurrentJobs; /* Maximum concurrent jobs */
-+ int64_t spool_size; /* Size of spool file for this job */
- int RescheduleTimes; /* Number of times to reschedule job */
- bool RescheduleOnError; /* Set to reschedule on error */
- bool PrefixLinks; /* prefix soft links with Where path */
-Index: src/stored/job.c
-===================================================================
---- src/stored/job.c (revision 5108)
-+++ src/stored/job.c (working copy)
-@@ -49,7 +49,7 @@
- /* Requests from the Director daemon */
- static char jobcmd[] = "JobId=%d job=%127s job_name=%127s client_name=%127s "
- "type=%d level=%d FileSet=%127s NoAttr=%d SpoolAttr=%d FileSetMD5=%127s "
-- "SpoolData=%d WritePartAfterJob=%d PreferMountedVols=%d\n";
-+ "SpoolData=%d SpoolSize=%s WritePartAfterJob=%d PreferMountedVols=%d\n";
-
-
- /* Responses sent to Director daemon */
-@@ -73,6 +73,7 @@
- {
- int JobId;
- char auth_key[100];
-+ char spool_size[30];
- BSOCK *dir = jcr->dir_bsock;
- POOL_MEM job_name, client_name, job, fileset_name, fileset_md5;
- int JobType, level, spool_attributes, no_attributes, spool_data;
-@@ -87,9 +88,9 @@
- stat = sscanf(dir->msg, jobcmd, &JobId, job.c_str(), job_name.c_str(),
- client_name.c_str(),
- &JobType, &level, fileset_name.c_str(), &no_attributes,
-- &spool_attributes, fileset_md5.c_str(), &spool_data,
-+ &spool_attributes, fileset_md5.c_str(), &spool_data, spool_size,
- &write_part_after_job, &PreferMountedVols);
-- if (stat != 13) {
-+ if (stat != 14) {
- pm_strcpy(jcr->errmsg, dir->msg);
- bnet_fsend(dir, BAD_job, stat, jcr->errmsg);
- Dmsg1(100, ">dird: %s", dir->msg);
-@@ -124,6 +125,7 @@
- jcr->no_attributes = no_attributes;
- jcr->spool_attributes = spool_attributes;
- jcr->spool_data = spool_data;
-+ jcr->spool_size = str_to_int64(spool_size);
- jcr->write_part_after_job = write_part_after_job;
- jcr->fileset_md5 = get_pool_memory(PM_NAME);
- pm_strcpy(jcr->fileset_md5, fileset_md5);
-Index: src/stored/acquire.c
-===================================================================
---- src/stored/acquire.c (revision 5108)
-+++ src/stored/acquire.c (working copy)
-@@ -609,7 +609,12 @@
- if (dcr->attached_to_dev) {
- detach_dcr_from_dev(dcr);
- }
-- dcr->max_job_spool_size = dev->device->max_job_spool_size;
-+ /* Use job spoolsize prior to device spoolsize*/
-+ if (jcr->spool_size) {
-+ dcr->max_job_spool_size = jcr->spool_size;
-+ } else {
-+ dcr->max_job_spool_size = dev->device->max_job_spool_size;
-+ }
- dcr->device = dev->device;
- dcr->dev = dev;
- attach_dcr_to_dev(dcr);
-Index: src/jcr.h
-===================================================================
---- src/jcr.h (revision 5108)
-+++ src/jcr.h (working copy)
-@@ -246,6 +246,7 @@
- int replace; /* Replace option */
- int NumVols; /* Number of Volume used in pool */
- int reschedule_count; /* Number of times rescheduled */
-+ int64_t spool_size; /* Spool size for this job */
- bool spool_data; /* Spool data in SD */
- bool acquired_resource_locks; /* set if resource locks acquired */
- bool term_wait_inited; /* Set when cond var inited */
-@@ -323,6 +324,7 @@
- bool spool_attributes; /* set if spooling attributes */
- bool no_attributes; /* set if no attributes wanted */
- bool spool_data; /* set to spool data */
-+ int64_t spool_size; /* Spool size for this job */
- int CurVol; /* Current Volume count */
- DIRRES* director; /* Director resource */
- alist *write_store; /* list of write storage devices sent by DIR */
+++ /dev/null
-From: Eric Bollengier <eric at homelinux dot org>
-
-Add Job { SpoolSize = 10G } option
-This is very useful for big backup
-
-You must upgrade SD and DIR at the same time.
-
*/
res_runscript.set_job_code_callback(job_code_callback_filesetname);
while ((c=(char*)res_runscript.commands->pop()) != NULL) {
- t = (int) res_runscript.commands->pop();
- res_runscript.command = c;
- res_runscript.cmd_type = t;
+ t = (int) res_runscript.commands->pop();
+ res_runscript.command = c;
+ res_runscript.cmd_type = t;
RUNSCRIPT *script = new_runscript();
memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
- /* target is taken from res_runscript, each runscript object have
- * a copy
- */
- script->target = NULL;
- script->set_target(res_runscript.target);
+ /* target is taken from res_runscript, each runscript object have
+ * a copy
+ */
+ script->target = NULL;
+ script->set_target(res_runscript.target);
(*runscripts)->append(script);
script->debug();