From dcb598c609204fcc77b0a8ea0fe0c5c73592f6e3 Mon Sep 17 00:00:00 2001 From: Eric Bollengier Date: Thu, 18 Nov 2010 14:30:23 +0100 Subject: [PATCH] Add delta sequence to batch mode, accurate query and file daemon ff_pkt --- bacula/src/cats/postgresql.c | 7 +- bacula/src/cats/sql_cmds.c | 225 +++++++++++++++-------------------- bacula/src/cats/sql_create.c | 24 ++-- bacula/src/cats/sql_get.c | 13 +- bacula/src/dird/backup.c | 18 +-- bacula/src/filed/accurate.c | 27 ++++- bacula/src/filed/backup.c | 16 +-- bacula/src/findlib/find.h | 1 + 8 files changed, 164 insertions(+), 167 deletions(-) diff --git a/bacula/src/cats/postgresql.c b/bacula/src/cats/postgresql.c index 959baf75ed..954b034978 100644 --- a/bacula/src/cats/postgresql.c +++ b/bacula/src/cats/postgresql.c @@ -749,7 +749,8 @@ int my_postgresql_batch_start(JCR *jcr, B_DB *mdb) "path varchar," "name varchar," "lstat varchar," - "md5 varchar)") == 1) + "md5 varchar," + "markid int)") == 1) { Dmsg0(500, "my_postgresql_batch_start failed\n"); return 1; @@ -857,9 +858,9 @@ int my_postgresql_batch_insert(JCR *jcr, B_DB *mdb, ATTR_DBR *ar) digest = ar->Digest; } - len = Mmsg(mdb->cmd, "%u\t%s\t%s\t%s\t%s\t%s\n", + len = Mmsg(mdb->cmd, "%u\t%s\t%s\t%s\t%s\t%s\t%u\n", ar->FileIndex, edit_int64(ar->JobId, ed1), mdb->esc_path, - mdb->esc_name, ar->attr, digest); + mdb->esc_name, ar->attr, digest, ar->DeltaSeq); do { res = PQputCopyData(mdb->db, diff --git a/bacula/src/cats/sql_cmds.c b/bacula/src/cats/sql_cmds.c index e94298c7a5..b0070cc63f 100644 --- a/bacula/src/cats/sql_cmds.c +++ b/bacula/src/cats/sql_cmds.c @@ -280,7 +280,7 @@ const char *uar_jobids_fileindex = const char *uar_jobid_fileindex_from_table = "SELECT JobId,FileIndex from %s"; -/* Get the list of the last recent version with a given jobid list +/* Get the list of the last recent version per Delta with a given jobid list * This is a tricky part because with SQL the result of * * SELECT MAX(A), B, C, D FROM... GROUP BY (B,C) @@ -290,10 +290,9 @@ const char *uar_jobid_fileindex_from_table = * With PostgreSQL, we can use DISTINCT ON(), but with Mysql or Sqlite, * we need an extra join using JobTDate. */ -const char *select_recent_version_with_basejob[5] = { - /* MySQL */ +const char *select_recent_version_with_basejob_default = "SELECT FileId, Job.JobId AS JobId, FileIndex, File.PathId AS PathId, " - "File.FilenameId AS FilenameId, LStat, MD5 " + "File.FilenameId AS FilenameId, LStat, MD5, MarkId " "FROM Job, File, ( " "SELECT MAX(JobTDate) AS JobTDate, PathId, FilenameId " "FROM ( " @@ -306,7 +305,8 @@ const char *select_recent_version_with_basejob[5] = { "JOIN File USING (FileId) " "JOIN Job ON (BaseJobId = Job.JobId) " "WHERE BaseFiles.JobId IN (%s) " /* Use Max(JobTDate) to find */ - ") AS tmp GROUP BY PathId, FilenameId " /* the latest file version */ + ") AS tmp " + "GROUP BY PathId, FilenameId " /* the latest file version */ ") AS T1 " "WHERE (Job.JobId IN ( " /* Security, we force JobId to be valid */ "SELECT DISTINCT BaseJobId FROM BaseFiles WHERE JobId IN (%s)) " @@ -314,102 +314,110 @@ const char *select_recent_version_with_basejob[5] = { "AND T1.JobTDate = Job.JobTDate " /* Join on JobTDate to get the orginal */ "AND Job.JobId = File.JobId " /* Job/File record */ "AND T1.PathId = File.PathId " - "AND T1.FilenameId = File.FilenameId", + "AND T1.FilenameId = File.FilenameId"; + +const char *select_recent_version_with_basejob[5] = { + /* MySQL */ + select_recent_version_with_basejob_default, /* Postgresql */ /* The DISTINCT ON () permits to avoid extra join */ "SELECT DISTINCT ON (FilenameId, PathId) StartTime, JobId, FileId, " - "FileIndex, PathId, FilenameId, LStat, MD5 " + "FileIndex, PathId, FilenameId, LStat, MD5, MarkId " "FROM " - "(SELECT FileId, JobId, PathId, FilenameId, FileIndex, LStat, MD5 " - "FROM File WHERE JobId IN (%s) " - "UNION ALL " - "SELECT File.FileId, File.JobId, PathId, FilenameId, " - "File.FileIndex, LStat, MD5 " - "FROM BaseFiles JOIN File USING (FileId) " - "WHERE BaseFiles.JobId IN (%s) " - ") AS T JOIN Job USING (JobId) " + "(SELECT FileId, JobId, PathId, FilenameId, FileIndex, LStat, MD5, MarkId " + "FROM File WHERE JobId IN (%s) " + "UNION ALL " + "SELECT File.FileId, File.JobId, PathId, FilenameId, " + "File.FileIndex, LStat, MD5, MarkId " + "FROM BaseFiles JOIN File USING (FileId) " + "WHERE BaseFiles.JobId IN (%s) " + ") AS T JOIN Job USING (JobId) " "ORDER BY FilenameId, PathId, StartTime DESC ", - /* SQLite */ /* See Mysql section for doc */ -"SELECT FileId, Job.JobId AS JobId, FileIndex, File.PathId AS PathId, " - "File.FilenameId AS FilenameId, LStat, MD5 " -"FROM Job, File, ( " - "SELECT MAX(JobTDate) AS JobTDate, PathId, FilenameId " - "FROM ( " - "SELECT JobTDate, PathId, FilenameId " - "FROM File JOIN Job USING (JobId) " - "WHERE File.JobId IN (%s) " - "UNION ALL " - "SELECT JobTDate, PathId, FilenameId " - "FROM BaseFiles " - "JOIN File USING (FileId) " - "JOIN Job ON (BaseJobId = Job.JobId) " - "WHERE BaseFiles.JobId IN (%s) " - ") AS tmp GROUP BY PathId, FilenameId " - ") AS T1 " -"WHERE (Job.JobId IN ( " - "SELECT DISTINCT BaseJobId FROM BaseFiles WHERE JobId IN (%s)) " - "OR Job.JobId IN (%s)) " - "AND T1.JobTDate = Job.JobTDate " - "AND Job.JobId = File.JobId " - "AND T1.PathId = File.PathId " - "AND T1.FilenameId = File.FilenameId", + /* SQLite */ + select_recent_version_with_basejob_default, - /* SQLite3 */ /* See Mysql section for doc */ -"SELECT FileId, Job.JobId AS JobId, FileIndex, File.PathId AS PathId, " - "File.FilenameId AS FilenameId, LStat, MD5 " -"FROM Job, File, ( " - "SELECT MAX(JobTDate) AS JobTDate, PathId, FilenameId " - "FROM ( " - "SELECT JobTDate, PathId, FilenameId " - "FROM File JOIN Job USING (JobId) " - "WHERE File.JobId IN (%s) " - "UNION ALL " - "SELECT JobTDate, PathId, FilenameId " - "FROM BaseFiles " - "JOIN File USING (FileId) " - "JOIN Job ON (BaseJobId = Job.JobId) " - "WHERE BaseFiles.JobId IN (%s) " - ") AS tmp GROUP BY PathId, FilenameId " - ") AS T1 " -"WHERE (Job.JobId IN ( " - "SELECT DISTINCT BaseJobId FROM BaseFiles WHERE JobId IN (%s)) " - "OR Job.JobId IN (%s)) " - "AND T1.JobTDate = Job.JobTDate " - "AND Job.JobId = File.JobId " - "AND T1.PathId = File.PathId " - "AND T1.FilenameId = File.FilenameId", + /* SQLite3 */ + select_recent_version_with_basejob_default, + + /* Ingres */ + select_recent_version_with_basejob_default +}; - /* Ingres */ /* See Mysql section for doc */ +/* We do the same thing than the previous query, but we include + * all delta parts. If the file has been deleted, we can have irrelevant + * parts. + * + * The code that uses results should control the delta sequence with + * the following rules: + * First Delta = 0 + * Delta = Previous Delta + 1 + * + * If we detect a gap, we can discard further pieces + * If a file starts at 1 instead of 0, the file has been deleted, and further + * pieces are useless. + * + * This control should be reset for each new file + */ +const char *select_recent_version_with_basejob_and_delta_default = "SELECT FileId, Job.JobId AS JobId, FileIndex, File.PathId AS PathId, " - "File.FilenameId AS FilenameId, LStat, MD5 " + "File.FilenameId AS FilenameId, LStat, MD5, MarkId " "FROM Job, File, ( " - "SELECT MAX(JobTDate) AS JobTDate, PathId, FilenameId " + "SELECT MAX(JobTDate) AS JobTDate, PathId, FilenameId, MarkId " "FROM ( " - "SELECT JobTDate, PathId, FilenameId " - "FROM File JOIN Job USING (JobId) " + "SELECT JobTDate, PathId, FilenameId " /* Get all normal files */ + "FROM File JOIN Job USING (JobId) " /* from selected backup */ "WHERE File.JobId IN (%s) " "UNION ALL " - "SELECT JobTDate, PathId, FilenameId " - "FROM BaseFiles " + "SELECT JobTDate, PathId, FilenameId " /* Get all files from */ + "FROM BaseFiles " /* BaseJob */ "JOIN File USING (FileId) " "JOIN Job ON (BaseJobId = Job.JobId) " - "WHERE BaseFiles.JobId IN (%s) " - ") AS tmp GROUP BY PathId, FilenameId " + "WHERE BaseFiles.JobId IN (%s) " /* Use Max(JobTDate) to find */ + ") AS tmp " + "GROUP BY PathId, FilenameId, MarkId " /* the latest file version */ ") AS T1 " -"WHERE (Job.JobId IN ( " - "SELECT DISTINCT BaseJobId FROM BaseFiles WHERE JobId IN (%s)) " +"WHERE (Job.JobId IN ( " /* Security, we force JobId to be valid */ + "SELECT DISTINCT BaseJobId FROM BaseFiles WHERE JobId IN (%s)) " "OR Job.JobId IN (%s)) " - "AND T1.JobTDate = Job.JobTDate " - "AND Job.JobId = File.JobId " + "AND T1.JobTDate = Job.JobTDate " /* Join on JobTDate to get the orginal */ + "AND Job.JobId = File.JobId " /* Job/File record */ "AND T1.PathId = File.PathId " - "AND T1.FilenameId = File.FilenameId" + "AND T1.FilenameId = File.FilenameId"; + +const char *select_recent_version_with_basejob_and_delta[5] = { + /* MySQL */ + select_recent_version_with_basejob_and_delta_default, + + /* Postgresql */ /* The DISTINCT ON () permits to avoid extra join */ + "SELECT DISTINCT ON (FilenameId, PathId, MarkId) JobTDate, JobId, FileId, " + "FileIndex, PathId, FilenameId, LStat, MD5, MarkId " + "FROM " + "(SELECT FileId, JobId, PathId, FilenameId, FileIndex, LStat, MD5, MarkId " + "FROM File WHERE JobId IN (%s) " + "UNION ALL " + "SELECT File.FileId, File.JobId, PathId, FilenameId, " + "File.FileIndex, LStat, MD5, MarkId " + "FROM BaseFiles JOIN File USING (FileId) " + "WHERE BaseFiles.JobId IN (%s) " + ") AS T JOIN Job USING (JobId) " + "ORDER BY FilenameId, PathId, MarkId, JobTDate DESC ", + + /* SQLite */ + select_recent_version_with_basejob_and_delta_default, + + /* SQLite3 */ + select_recent_version_with_basejob_and_delta_default, + + /* Ingres */ + select_recent_version_with_basejob_and_delta_default }; -/* Get the list of the last recent version with a given BaseJob jobid list */ -const char *select_recent_version[5] = { - /* MySQL */ - "SELECT j1.JobId AS JobId, f1.FileId AS FileId, f1.FileIndex AS FileIndex, " +/* Get the list of the last recent version with a given BaseJob jobid list + * We don't handle Delta with BaseJobs, they have only Full files + */ +const char *select_recent_version_default = + "SELECT j1.JobId AS JobId, f1.FileId AS FileId, f1.FileIndex AS FileIndex, " "f1.PathId AS PathId, f1.FilenameId AS FilenameId, " "f1.LStat AS LStat, f1.MD5 AS MD5 " "FROM ( " /* Choose the last version for each Path/Filename */ @@ -422,62 +430,27 @@ const char *select_recent_version[5] = { "AND j1.JobId IN (%s) " "AND t1.FilenameId = f1.FilenameId " "AND t1.PathId = f1.PathId " - "AND j1.JobId = f1.JobId", + "AND j1.JobId = f1.JobId"; + +const char *select_recent_version[5] = { + /* MySQL */ + select_recent_version_default, /* Postgresql */ - "SELECT DISTINCT ON (FilenameId, PathId) StartTime, JobId, FileId, " + "SELECT DISTINCT ON (FilenameId, PathId) JobTDate, JobId, FileId, " "FileIndex, PathId, FilenameId, LStat, MD5 " "FROM File JOIN Job USING (JobId) " "WHERE JobId IN (%s) " - "ORDER BY FilenameId, PathId, StartTime DESC ", + "ORDER BY FilenameId, PathId, JobTDate DESC ", /* SQLite */ - "SELECT j1.JobId AS JobId, f1.FileId AS FileId, f1.FileIndex AS FileIndex, " - "f1.PathId AS PathId, f1.FilenameId AS FilenameId, " - "f1.LStat AS LStat, f1.MD5 AS MD5 " - "FROM ( " - "SELECT max(JobTDate) AS JobTDate, PathId, FilenameId " - "FROM File JOIN Job USING (JobId) " - "WHERE File.JobId IN (%s) " - "GROUP BY PathId, FilenameId " - ") AS t1, Job AS j1, File AS f1 " - "WHERE t1.JobTDate = j1.JobTDate " - "AND j1.JobId IN (%s) " - "AND t1.FilenameId = f1.FilenameId " - "AND t1.PathId = f1.PathId " - "AND j1.JobId = f1.JobId", + select_recent_version_default, /* SQLite3 */ - "SELECT j1.JobId AS JobId, f1.FileId AS FileId, f1.FileIndex AS FileIndex, " - "f1.PathId AS PathId, f1.FilenameId AS FilenameId, " - "f1.LStat AS LStat, f1.MD5 AS MD5 " - "FROM ( " - "SELECT max(JobTDate) AS JobTDate, PathId, FilenameId " - "FROM File JOIN Job USING (JobId) " - "WHERE File.JobId IN (%s) " - "GROUP BY PathId, FilenameId " - ") AS t1, Job AS j1, File AS f1 " - "WHERE t1.JobTDate = j1.JobTDate " - "AND j1.JobId IN (%s) " - "AND t1.FilenameId = f1.FilenameId " - "AND t1.PathId = f1.PathId " - "AND j1.JobId = f1.JobId", + select_recent_version_default, /* Ingres */ - "SELECT j1.JobId AS JobId, f1.FileId AS FileId, f1.FileIndex AS FileIndex, " - "f1.PathId AS PathId, f1.FilenameId AS FilenameId, " - "f1.LStat AS LStat, f1.MD5 AS MD5 " - "FROM ( " /* Choose the last version for each Path/Filename */ - "SELECT max(JobTDate) AS JobTDate, PathId, FilenameId " - "FROM File JOIN Job USING (JobId) " - "WHERE File.JobId IN (%s) " - "GROUP BY PathId, FilenameId " - ") AS t1, Job AS j1, File AS f1 " - "WHERE t1.JobTDate = j1.JobTDate " - "AND j1.JobId IN (%s) " - "AND t1.FilenameId = f1.FilenameId " - "AND t1.PathId = f1.PathId " - "AND j1.JobId = f1.JobId" + select_recent_version_default }; /* We don't create this table as TEMPORARY because MySQL MyISAM diff --git a/bacula/src/cats/sql_create.c b/bacula/src/cats/sql_create.c index 3dcc13c81e..1998aebec7 100644 --- a/bacula/src/cats/sql_create.c +++ b/bacula/src/cats/sql_create.c @@ -763,7 +763,8 @@ bool my_batch_start(JCR *jcr, B_DB *mdb) "Path blob," "Name blob," "LStat tinyblob," - "MD5 tinyblob)",NULL, NULL); + "MD5 tinyblob," + "MarkId integer)",NULL, NULL); db_unlock(mdb); return ok; } @@ -790,9 +791,10 @@ bool my_batch_insert(JCR *jcr, B_DB *mdb, ATTR_DBR *ar) digest = ar->Digest; } - len = Mmsg(mdb->cmd, "INSERT INTO batch VALUES (%u,%s,'%s','%s','%s','%s')", + len = Mmsg(mdb->cmd, "INSERT INTO batch VALUES " + "(%u,%s,'%s','%s','%s','%s',%u)", ar->FileIndex, edit_int64(ar->JobId,ed1), mdb->esc_path, - mdb->esc_name, ar->attr, digest); + mdb->esc_name, ar->attr, digest, ar->DeltaSeq); return INSERT_DB(jcr, mdb, mdb->cmd); } @@ -884,12 +886,12 @@ bool db_write_batch_file_records(JCR *jcr) } if (!db_sql_query(jcr->db_batch, - "INSERT INTO File (FileIndex, JobId, PathId, FilenameId, LStat, MD5) " - "SELECT batch.FileIndex, batch.JobId, Path.PathId, " - "Filename.FilenameId,batch.LStat, batch.MD5 " - "FROM batch " - "JOIN Path ON (batch.Path = Path.Path) " - "JOIN Filename ON (batch.Name = Filename.Name)", + "INSERT INTO File (FileIndex, JobId, PathId, FilenameId, LStat, MD5, MarkId) " + "SELECT batch.FileIndex, batch.JobId, Path.PathId, " + "Filename.FilenameId,batch.LStat, batch.MD5, batch.MarkId " + "FROM batch " + "JOIN Path ON (batch.Path = Path.Path) " + "JOIN Filename ON (batch.Name = Filename.Name)", NULL,NULL)) { Jmsg1(jcr, M_FATAL, 0, "Fill File table %s\n", jcr->db_batch->errmsg); @@ -1023,9 +1025,9 @@ static int db_create_file_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar) /* Must create it */ Mmsg(mdb->cmd, "INSERT INTO File (FileIndex,JobId,PathId,FilenameId," - "LStat,MD5) VALUES (%u,%u,%u,%u,'%s','%s')", + "LStat,MD5,MarkId) VALUES (%u,%u,%u,%u,'%s','%s',%u)", ar->FileIndex, ar->JobId, ar->PathId, ar->FilenameId, - ar->attr, digest); + ar->attr, digest, ar->DeltaSeq); ar->FileId = sql_insert_autokey_record(mdb, mdb->cmd, NT_("File")); if (ar->FileId == 0) { diff --git a/bacula/src/cats/sql_get.c b/bacula/src/cats/sql_get.c index f5b226d902..069fb0b321 100644 --- a/bacula/src/cats/sql_get.c +++ b/bacula/src/cats/sql_get.c @@ -1111,14 +1111,15 @@ bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids, Mmsg(buf2, select_recent_version_with_basejob[db_type], jobids, jobids, jobids, jobids); Mmsg(buf, -"SELECT Path.Path, Filename.Name, Temp.FileIndex, Temp.JobId, LStat, MD5 " - "FROM ( %s ) AS Temp " - "JOIN Filename ON (Filename.FilenameId = Temp.FilenameId) " - "JOIN Path ON (Path.PathId = Temp.PathId) " +"SELECT Path.Path, Filename.Name, T1.FileIndex, T1.JobId, LStat, MD5, MarkId " + "FROM ( %s ) AS T1 " + "JOIN Filename ON (Filename.FilenameId = T1.FilenameId) " + "JOIN Path ON (Path.PathId = T1.PathId) " "WHERE FileIndex > 0 " -"ORDER BY Temp.JobId, FileIndex ASC",/* Return sorted by JobId, */ +"ORDER BY T1.JobId, FileIndex ASC",/* Return sorted by JobId, */ /* FileIndex for restore code */ buf2.c_str()); + Dmsg1(100, "q=%s\n", buf.c_str()); #else /* * I am not sure that this works the same as the code in ua_restore.c but it @@ -1243,7 +1244,7 @@ bool db_get_base_file_list(JCR *jcr, B_DB *mdb, POOL_MEM buf(PM_MESSAGE); Mmsg(buf, - "SELECT Path, Name, FileIndex, JobId, LStat, MD5 " + "SELECT Path, Name, FileIndex, JobId, LStat, MD5, 0 As MarkId " "FROM new_basefile%lld ORDER BY JobId, FileIndex ASC", (uint64_t) jcr->JobId); diff --git a/bacula/src/dird/backup.c b/bacula/src/dird/backup.c index 746346abfb..9467a478b9 100644 --- a/bacula/src/dird/backup.c +++ b/bacula/src/dird/backup.c @@ -137,7 +137,7 @@ static bool get_base_jobids(JCR *jcr, db_list_ctx *jobids) } /* - * Foreach files in currrent list, send "/path/fname\0LStat\0MD5" to FD + * Foreach files in currrent list, send "/path/fname\0LStat\0MD5\0Delta" to FD */ static int accurate_list_handler(void *ctx, int num_fields, char **row) { @@ -147,21 +147,21 @@ static int accurate_list_handler(void *ctx, int num_fields, char **row) return 1; } - if (row[2] == 0) { /* discard when file_index == 0 */ + if (row[2][0] == '0') { /* discard when file_index == 0 */ return 0; } /* sending with checksum */ if (jcr->use_accurate_chksum - && num_fields == 6 + && num_fields == 7 && row[5][0] /* skip checksum = '0' */ && row[5][1]) { - jcr->file_bsock->fsend("%s%s%c%s%c%s", - row[0], row[1], 0, row[4], 0, row[5]); + jcr->file_bsock->fsend("%s%s%c%s%c%s%c%s", + row[0], row[1], 0, row[4], 0, row[5], 0, row[6]); } else { - jcr->file_bsock->fsend("%s%s%c%s", - row[0], row[1], 0, row[4]); + jcr->file_bsock->fsend("%s%s%c%s%c%c%s", + row[0], row[1], 0, row[4], 0, 0, row[6]); } return 0; } @@ -229,8 +229,8 @@ static bool is_checksum_needed_by_fileset(JCR *jcr) /* * Send current file list to FD * DIR -> FD : accurate files=xxxx - * DIR -> FD : /path/to/file\0Lstat\0MD5 - * DIR -> FD : /path/to/dir/\0Lstat\0MD5 + * DIR -> FD : /path/to/file\0Lstat\0MD5\0Delta + * DIR -> FD : /path/to/dir/\0Lstat\0MD5\0Delta * ... * DIR -> FD : EOD */ diff --git a/bacula/src/filed/accurate.c b/bacula/src/filed/accurate.c index edaa3c2a19..bd2fa1a9e0 100644 --- a/bacula/src/filed/accurate.c +++ b/bacula/src/filed/accurate.c @@ -40,6 +40,7 @@ typedef struct PrivateCurFile { char *fname; char *lstat; char *chksum; + int32_t delta_seq; bool seen; } CurFile; @@ -199,7 +200,8 @@ bool accurate_finish(JCR *jcr) } static bool accurate_add_file(JCR *jcr, uint32_t len, - char *fname, char *lstat, char *chksum) + char *fname, char *lstat, char *chksum, + int32_t delta) { bool ret = true; CurFile elt; @@ -219,9 +221,12 @@ static bool accurate_add_file(JCR *jcr, uint32_t len, item->chksum = item->lstat+strlen(item->lstat)+1; strcpy(item->chksum, chksum); + item->delta_seq = delta; + jcr->file_list->insert(item->fname, item); - Dmsg3(dbglvl, "add fname=<%s> lstat=%s chksum=%s\n", fname, lstat, chksum); + Dmsg4(dbglvl, "add fname=<%s> lstat=%s delta_seq=%i chksum=%s\n", + fname, lstat, delta, chksum); return ret; } @@ -246,6 +251,8 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt) char *fname; CurFile elt; + ff_pkt->delta_seq = 0; + if (!jcr->accurate) { return true; } @@ -264,6 +271,9 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt) goto bail_out; } + ff_pkt->delta_seq = elt.delta_seq; + Dmsg1(10, "accurate delta_seq=%i\n", ff_pkt->delta_seq); + if (elt.seen) { /* file has been seen ? */ Dmsg1(dbglvl, "accurate %s (already seen)\n", fname); goto bail_out; @@ -479,6 +489,7 @@ int accurate_cmd(JCR *jcr) BSOCK *dir = jcr->dir_bsock; int lstat_pos, chksum_pos; int32_t nb; + uint16_t delta_seq; if (job_canceled(jcr)) { return true; @@ -494,7 +505,7 @@ int accurate_cmd(JCR *jcr) /* * buffer = sizeof(CurFile) + dirmsg - * dirmsg = fname + \0 + lstat + \0 + checksum + \0 + * dirmsg = fname + \0 + lstat + \0 + checksum + \0 + delta_seq + \0 */ /* get current files */ while (dir->recv() >= 0) { @@ -504,12 +515,18 @@ int accurate_cmd(JCR *jcr) if (chksum_pos >= dir->msglen) { chksum_pos = lstat_pos - 1; /* tweak: no checksum, point to the last \0 */ - } + delta_seq = 0; + } else { + delta_seq = str_to_int32(dir->msg + + chksum_pos + + strlen(dir->msg + chksum_pos) + 1); + } accurate_add_file(jcr, dir->msglen, dir->msg, /* Path */ dir->msg + lstat_pos, /* LStat */ - dir->msg + chksum_pos); /* CheckSum */ + dir->msg + chksum_pos, /* CheckSum */ + delta_seq); /* Delta Sequence */ } } diff --git a/bacula/src/filed/backup.c b/bacula/src/filed/backup.c index 9dcf15efee..3116d7f62e 100644 --- a/bacula/src/filed/backup.c +++ b/bacula/src/filed/backup.c @@ -1187,15 +1187,16 @@ bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream) case FT_LNK: case 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, - ff_pkt->type, ff_pkt->fname, 0, attribs, 0, ff_pkt->link, 0, - attribsEx, 0); + stat = sd->fsend("%ld %d %s%c%s%c%s%c%s%c%u%c", jcr->JobFiles, + ff_pkt->type, ff_pkt->fname, 0, attribs, 0, + ff_pkt->link, 0, attribsEx, 0, ff_pkt->delta_seq, 0); break; case FT_DIREND: case FT_REPARSE: /* Here link is the canonical filename (i.e. with trailing slash) */ - stat = sd->fsend("%ld %d %s%c%s%c%c%s%c", jcr->JobFiles, - ff_pkt->type, ff_pkt->link, 0, attribs, 0, 0, attribsEx, 0); + stat = sd->fsend("%ld %d %s%c%s%c%c%s%c%u%c", jcr->JobFiles, + ff_pkt->type, ff_pkt->link, 0, attribs, 0, 0, + attribsEx, 0, ff_pkt->delta_seq, 0); break; case FT_RESTORE_FIRST: comp_len = ff_pkt->object_len; @@ -1229,8 +1230,9 @@ bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream) } break; default: - 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); + stat = sd->fsend("%ld %d %s%c%s%c%c%s%c%u%c", jcr->JobFiles, + ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0, + attribsEx, 0, ff_pkt->delta_seq, 0); break; } if (ff_pkt->type != FT_DELETED) { diff --git a/bacula/src/findlib/find.h b/bacula/src/findlib/find.h index 6a57528a2e..4939d7446c 100644 --- a/bacula/src/findlib/find.h +++ b/bacula/src/findlib/find.h @@ -197,6 +197,7 @@ struct FF_PKT { struct stat statp; /* stat packet */ int32_t FileIndex; /* FileIndex of this file */ int32_t LinkFI; /* FileIndex of main hard linked file */ + int32_t delta_seq; /* Delta Sequence number */ int32_t object_index; /* Object index */ int32_t object_len; /* Object length */ int32_t object_compression; /* Type of compression for object */ -- 2.39.5