From 6999c8e8e2a1e9d221f3301d7ce49eb0aaa4dcee Mon Sep 17 00:00:00 2001 From: Eric Bollengier Date: Thu, 13 Mar 2008 14:21:25 +0000 Subject: [PATCH] ebl Cleanup git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@6601 91ce42f0-d328-0410-95d8-f526ca767f89 --- .../patches/testing/clientrunaftervss.patch | 87 - ...roject-accurate-backup-without-lstat.patch | 184 -- .../testing/project-accurate-backup.patch | 1615 ----------------- .../testing/project-accurate-backup.patch2 | 1148 ------------ .../testing/project-accurate-backup.sql | 70 - .../patches/testing/spoolsize_per_job.patch | 159 -- .../patches/testing/spoolsize_per_job.readme | 7 - bacula/src/dird/dird_conf.c | 16 +- 8 files changed, 8 insertions(+), 3278 deletions(-) delete mode 100644 bacula/patches/testing/clientrunaftervss.patch delete mode 100644 bacula/patches/testing/project-accurate-backup-without-lstat.patch delete mode 100644 bacula/patches/testing/project-accurate-backup.patch delete mode 100644 bacula/patches/testing/project-accurate-backup.patch2 delete mode 100644 bacula/patches/testing/project-accurate-backup.sql delete mode 100644 bacula/patches/testing/spoolsize_per_job.patch delete mode 100644 bacula/patches/testing/spoolsize_per_job.readme diff --git a/bacula/patches/testing/clientrunaftervss.patch b/bacula/patches/testing/clientrunaftervss.patch deleted file mode 100644 index 847f76f7f1..0000000000 --- a/bacula/patches/testing/clientrunaftervss.patch +++ /dev/null @@ -1,87 +0,0 @@ - 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 - ./configure - patch -p0 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)) diff --git a/bacula/patches/testing/project-accurate-backup-without-lstat.patch b/bacula/patches/testing/project-accurate-backup-without-lstat.patch deleted file mode 100644 index 667c13cf26..0000000000 --- a/bacula/patches/testing/project-accurate-backup-without-lstat.patch +++ /dev/null @@ -1,184 +0,0 @@ -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); diff --git a/bacula/patches/testing/project-accurate-backup.patch b/bacula/patches/testing/project-accurate-backup.patch deleted file mode 100644 index 4ec8610fda..0000000000 --- a/bacula/patches/testing/project-accurate-backup.patch +++ /dev/null @@ -1,1615 +0,0 @@ -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, _("birdmsglen, 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, _("bdirddb, 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 -+ * -+ */ -+ 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} $* <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, " 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) diff --git a/bacula/patches/testing/project-accurate-backup.patch2 b/bacula/patches/testing/project-accurate-backup.patch2 deleted file mode 100644 index 01767e7827..0000000000 --- a/bacula/patches/testing/project-accurate-backup.patch2 +++ /dev/null @@ -1,1148 +0,0 @@ -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, "dirdattr = 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)); diff --git a/bacula/patches/testing/project-accurate-backup.sql b/bacula/patches/testing/project-accurate-backup.sql deleted file mode 100644 index ae5181d5e1..0000000000 --- a/bacula/patches/testing/project-accurate-backup.sql +++ /dev/null @@ -1,70 +0,0 @@ - -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 - diff --git a/bacula/patches/testing/spoolsize_per_job.patch b/bacula/patches/testing/spoolsize_per_job.patch deleted file mode 100644 index e909573b1f..0000000000 --- a/bacula/patches/testing/spoolsize_per_job.patch +++ /dev/null @@ -1,159 +0,0 @@ -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, "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 */ diff --git a/bacula/patches/testing/spoolsize_per_job.readme b/bacula/patches/testing/spoolsize_per_job.readme deleted file mode 100644 index cdc86d84b5..0000000000 --- a/bacula/patches/testing/spoolsize_per_job.readme +++ /dev/null @@ -1,7 +0,0 @@ -From: Eric Bollengier - -Add Job { SpoolSize = 10G } option -This is very useful for big backup - -You must upgrade SD and DIR at the same time. - diff --git a/bacula/src/dird/dird_conf.c b/bacula/src/dird/dird_conf.c index 1c5fd5ab02..a50001a0c6 100644 --- a/bacula/src/dird/dird_conf.c +++ b/bacula/src/dird/dird_conf.c @@ -1940,16 +1940,16 @@ static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass) */ 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(); -- 2.39.5