From 120ce817804d1cf223a2cb8fbdb29247897d4024 Mon Sep 17 00:00:00 2001 From: Eric Bollengier Date: Mon, 18 Feb 2008 23:06:04 +0000 Subject: [PATCH] ebl new version of accurate project git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@6444 91ce42f0-d328-0410-95d8-f526ca767f89 --- .../testing/project-accurate-backup.patch2 | 975 ++++++++++++++++++ 1 file changed, 975 insertions(+) create mode 100644 bacula/patches/testing/project-accurate-backup.patch2 diff --git a/bacula/patches/testing/project-accurate-backup.patch2 b/bacula/patches/testing/project-accurate-backup.patch2 new file mode 100644 index 0000000000..7c708416ad --- /dev/null +++ b/bacula/patches/testing/project-accurate-backup.patch2 @@ -0,0 +1,975 @@ +Index: src/dird/fd_cmds.c +=================================================================== +--- src/dird/fd_cmds.c (révision 6443) ++++ 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,7 +226,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); + } +@@ -240,24 +240,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 6443) ++++ 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,81 @@ + 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_accurate_get_jobids(JCR *jcr, POOLMEM *jobids) ++{ ++ pm_strcpy(jobids, "1"); ++ return 1; ++} ++ ++bool send_accurate_current_files(JCR *jcr) ++{ ++ char buf[MAXSTRING]; ++ char ed1[50], ed2[50]; ++ ++ POOLMEM *jobids = get_pool_memory(PM_FNAME); ++ db_accurate_get_jobids(jcr, jobids); ++ ++ bsnprintf(buf, sizeof(buf), ++ "CREATE TEMPORARY TABLE btemp2%s AS ( " ++ "SELECT max(FileId) as FileId, PathId, FilenameId " ++ "FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (%s)) AS F " ++ "GROUP BY PathId, FilenameId ) ", ++ edit_uint64(jcr->JobId, ed1), ++ jobids); ++ db_sql_query(jcr->db, buf, NULL, NULL); ++ ++ // TODO: compter le nombre de rows ++ jcr->file_bsock->fsend("accurate files=%s", edit_uint64(5895, ed2)); /* TODO: change protocol to something like nb= */ ++ ++ bsnprintf(buf, sizeof(buf), ++ "SELECT File.FileIndex, Path.Path, Filename.Name, File.LStat " ++ "FROM btemp2%s JOIN Path USING (PathId) JOIN Filename USING (FilenameId) " ++ "JOIN File USING (FileId) " ++ "WHERE File.FileIndex > 0", ++ ed1, jobids); ++ db_sql_query(jcr->db, buf, accurate_list_handler, (void *)jcr); ++ ++ bsnprintf(buf, sizeof(buf), "DROP TABLE btemp2%s", ed1); ++ free_pool_memory(jobids); ++ + /* ++ 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 ++*/ ++ ++ jcr->file_bsock->signal(BNET_EOD); ++ return true; ++} ++ ++/* + * Do a backup of the specified FileSet + * + * Returns: false on failure +@@ -225,6 +300,14 @@ + Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db)); + } + ++ /* ++ * If backup is in accurate mode, FD will send the list of ++ * all files. ++ */ ++ if (!send_accurate_current_files(jcr)) { ++ goto bail_out; ++ } ++ + /* Send backup command */ + fd->fsend(backupcmd); + if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) { +@@ -234,6 +317,7 @@ + /* Pickup Job termination data */ + stat = wait_for_job_termination(jcr); + db_write_batch_file_records(jcr); /* used by bulk batch file insert */ ++ + if (stat == JS_Terminated) { + backup_cleanup(jcr, stat); + return true; +Index: src/dird/inc_conf.c +=================================================================== +--- src/dird/inc_conf.c (révision 6443) ++++ src/dird/inc_conf.c (copie de travail) +@@ -94,6 +94,7 @@ + * Items that are valid in an Options resource + */ + static RES_ITEM options_items[] = { ++ {"accurate", store_opts, {0}, 0, 0, 0}, + {"compression", store_opts, {0}, 0, 0, 0}, + {"signature", store_opts, {0}, 0, 0, 0}, + {"verify", store_opts, {0}, 0, 0, 0}, +@@ -153,7 +154,8 @@ + INC_KW_NOATIME, + INC_KW_ENHANCEDWILD, + INC_KW_CHKCHANGES, +- INC_KW_STRIPPATH ++ INC_KW_STRIPPATH, ++ INC_KW_ACCURATE + }; + + /* +@@ -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 6443) ++++ src/filed/backup.c (copie de travail) +@@ -48,8 +48,175 @@ + 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); + ++ + /* ++ * 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, FF_PKT *ff_pkt, char *attr) ++{ ++ char *p; ++ int stat=false; ++ struct stat statf; /* file stat */ ++ struct stat statc; /* catalog stat */ ++ char *Opts_Digest = ff_pkt->VerifyOpts; ++ char *lstat; ++ ++ int32_t LinkFIf, LinkFIc; ++ ++ if (jcr->accurate == false || jcr->JobLevel == L_FULL) { ++ return attr != NULL; /* if null => don't backup */ ++ } ++ ++ return attr != true; ++ ++ decode_stat(attr, &statf, &LinkFIf); /* decode file stat packet */ ++ ++ // TODO: check for /path/ and /path/file ++ lstat = (char *) jcr->file_list->lookup(ff_pkt->fname); ++ ++ if (!lstat) { ++ // TODO: we must backup it ! ++ return true; ++ } ++ ++ decode_stat(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; ++ } ++ } ++ *lstat = '\0'; /* mark it as deleted */ ++ return stat; ++} ++ ++#include "lib/htable.c" ++int accurate_get_current_file_list_cmd(JCR *jcr) ++{ ++ BSOCK *dir = jcr->dir_bsock; ++ char *lstat; ++ int len; ++ uint64_t nb; ++ ++ if (jcr->accurate == false || job_canceled(jcr)) { ++ 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(jcr, &jcr->link, nb); ++ ++ /* get current files */ ++ while (dir->recv() >= 0) { ++ len = strlen(dir->msg); ++ if ((len+1) < dir->msglen) { ++ char *elt = (char *) malloc(dir->msglen+1); ++ memcpy(elt, dir->msg, dir->msglen+1); ++ lstat = elt + len + 1; ++ Dmsg2(5, "hash[%s]=%s\n", elt, lstat); ++ jcr->file_list->insert(elt, lstat); ++ } ++ } ++ ++ jcr->file_list->stats(); ++// dir->fsend("2000 OK accurate\n"); ++ ++ return true; ++} ++ ++/* + * Find all the requested files and send them + * to the Storage daemon. + * +@@ -66,7 +233,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,7 +300,12 @@ + ok = false; /* error */ + set_jcr_job_status(jcr, JS_ErrorTerminated); + } ++ Dmsg1(1, "jcr->accurate == %i\n", jcr->accurate); + ++ if (jcr->accurate) { ++ //accurate_send_deleted_list(jcr); /* send deleted list to SD */ ++ } ++ + free_pool_memory(jcr->acl_text); + + stop_heartbeat_monitor(jcr); +@@ -355,9 +526,11 @@ + case FT_DIRNOCHG: + case FT_NOCHG: + Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname); ++ accurate_check_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_check_file(jcr, ff_pkt, NULL); /* list skipped files */ + return 1; + case FT_NOOPEN: { + berrno be; +@@ -1118,6 +1291,9 @@ + } + unstrip_path(ff_pkt); + ++ /* list backuped files */ ++ accurate_check_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"), +@@ -1128,6 +1304,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 6443) ++++ src/filed/job.c (copie de travail) +@@ -49,6 +49,7 @@ + /* Imported functions */ + extern int status_cmd(JCR *jcr); + extern int qstatus_cmd(JCR *jcr); ++extern int accurate_get_current_file_list_cmd(JCR *jcr); + + /* 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_get_current_file_list_cmd, 0}, + {NULL, NULL} /* list terminator */ + }; + +@@ -1087,6 +1089,9 @@ + case 'c': + fo->flags |= FO_CHKCHANGES; + break; ++ case 'C': ++ fo->flags |= FO_ACCURATE; ++ break; + default: + Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p); + break; +@@ -1195,6 +1200,9 @@ + + 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 +1212,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 6443) ++++ 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 6443) ++++ 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} $* <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 6443) ++++ 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 6443) ++++ 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,6 @@ + return stat; + } + +- + /* + * Get a File record + * Returns: 0 on failure +Index: src/stored/append.c +=================================================================== +--- src/stored/append.c (révision 6443) ++++ src/stored/append.c (copie de travail) +@@ -146,7 +146,7 @@ + + /* Read Stream header from the File daemon. + * The stream header consists of the following: +- * file_index (sequential Bacula file index, base 1) ++ * file_index (sequential Bacula file index, base 1, 0 for deleted files) + * stream (Bacula number to distinguish parts of data) + * info (Info for Storage daemon -- compressed, encryped, ...) + * info is not currently used, so is read, but ignored! +@@ -185,16 +185,18 @@ + + 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,25 +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); ++ while (!write_record_to_block(dcr->block, &rec)) { ++ Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len, ++ rec.remainder); ++ if (!write_block_to_device(dcr)) { ++ Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n", ++ dev->print_name(), dev->bstrerror()); ++ ok = false; ++ break; ++ } + ++ if (!ok) { ++ Dmsg0(400, "Not OK\n"); ++ break; ++ } ++ jcr->JobBytes += rec.data_len; /* increment bytes this job */ ++ Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n", ++ FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId, ++ stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len); ++ } ++ + /* Send attributes and digest to Director for Catalog */ + if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX || + crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) { +Index: src/jcr.h +=================================================================== +--- src/jcr.h (révision 6443) ++++ src/jcr.h (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/findlib/find.h +=================================================================== +--- src/findlib/find.h (révision 6443) ++++ 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; -- 2.39.5